From 6bf9f92302dc199bfe881e3b646a7ae58b125741 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 26 Oct 2015 15:33:21 +0100 Subject: [PATCH] Dev Updated Yii to 1.1.16, reapplied CDbHttpSession patch by c_schmitz. --- framework/CHANGELOG | 1660 -- framework/LICENSE | 31 - framework/README | 64 - framework/UPGRADE | 502 - framework/YiiBase.php | 15 +- framework/base/CApplication.php | 13 +- framework/base/CComponent.php | 4 +- framework/base/CErrorHandler.php | 85 +- framework/base/CModel.php | 2 +- framework/base/CModule.php | 23 +- framework/base/CSecurityManager.php | 144 +- framework/base/interfaces.php | 2 +- framework/caching/CApcCache.php | 3 + framework/caching/CFileCache.php | 27 +- framework/caching/CRedisCache.php | 2 +- framework/cli/commands/MessageCommand.php | 25 +- framework/cli/commands/MigrateCommand.php | 50 +- framework/cli/commands/ShellCommand.php | 30 +- framework/cli/views/shell/crud/view.php | 2 +- .../views/webapp/protected/config/console.php | 20 +- .../views/webapp/protected/config/main.php | 23 +- .../webapp/protected/views/layouts/main.php | 18 +- framework/cli/views/webapp/protected/yiic | 0 framework/console/CConsoleApplication.php | 2 +- framework/console/CConsoleCommand.php | 4 +- framework/db/CDbCommand.php | 87 +- framework/db/CDbConnection.php | 55 +- framework/db/CDbMigration.php | 39 +- framework/db/ar/CActiveFinder.php | 39 +- framework/db/ar/CActiveRecord.php | 74 +- framework/db/ar/CActiveRecordBehavior.php | 2 +- framework/db/schema/CDbCommandBuilder.php | 13 +- framework/db/schema/CDbCriteria.php | 16 +- framework/db/schema/CDbSchema.php | 46 +- .../db/schema/mssql/CMssqlCommandBuilder.php | 4 +- framework/db/schema/mssql/CMssqlSchema.php | 2 + .../db/schema/mysql/CMysqlCommandBuilder.php | 15 + framework/db/schema/mysql/CMysqlSchema.php | 2 + .../db/schema/oci/COciCommandBuilder.php | 1 - framework/db/schema/oci/COciSchema.php | 4 +- .../db/schema/pgsql/CPgsqlColumnSchema.php | 26 +- framework/db/schema/pgsql/CPgsqlSchema.php | 40 +- framework/db/schema/sqlite/CSqliteSchema.php | 2 + framework/gii/CCodeGenerator.php | 2 +- framework/gii/assets/js/tooltip.js | 5 +- framework/gii/components/Pear/Text/Diff.php | 2 +- .../gii/components/Pear/Text/Diff/Mapped.php | 2 +- .../generators/controller/ControllerCode.php | 2 +- framework/gii/generators/crud/CrudCode.php | 2 +- framework/gii/generators/model/ModelCode.php | 6 +- .../model/templates/default/model.php | 2 +- .../gii/generators/module/ModuleCode.php | 2 +- framework/gii/views/common/code.php | 2 +- framework/i18n/CDateFormatter.php | 2 +- framework/i18n/CDbMessageSource.php | 15 +- framework/i18n/CGettextMessageSource.php | 2 +- framework/i18n/CLocale.php | 4 +- framework/i18n/data/README.txt | 6 +- framework/i18n/data/aa.php | 34 +- framework/i18n/data/aa_dj.php | 34 +- framework/i18n/data/aa_er.php | 34 +- framework/i18n/data/aa_et.php | 34 +- framework/i18n/data/af.php | 76 +- framework/i18n/data/af_na.php | 74 +- framework/i18n/data/af_za.php | 76 +- framework/i18n/data/agq.php | 12 +- framework/i18n/data/agq_cm.php | 12 +- framework/i18n/data/ak.php | 13 +- framework/i18n/data/ak_gh.php | 13 +- framework/i18n/data/am.php | 211 +- framework/i18n/data/am_et.php | 211 +- framework/i18n/data/ar.php | 170 +- framework/i18n/data/ar_001.php | 170 +- framework/i18n/data/ar_ae.php | 170 +- framework/i18n/data/ar_bh.php | 170 +- framework/i18n/data/ar_dz.php | 209 +- framework/i18n/data/ar_eg.php | 170 +- framework/i18n/data/ar_iq.php | 233 +- framework/i18n/data/ar_jo.php | 170 +- framework/i18n/data/ar_kw.php | 170 +- framework/i18n/data/ar_lb.php | 166 +- framework/i18n/data/ar_ly.php | 166 +- framework/i18n/data/ar_ma.php | 205 +- framework/i18n/data/ar_om.php | 170 +- framework/i18n/data/ar_qa.php | 170 +- framework/i18n/data/ar_sa.php | 170 +- framework/i18n/data/ar_sd.php | 170 +- framework/i18n/data/ar_sy.php | 170 +- framework/i18n/data/ar_tn.php | 208 +- framework/i18n/data/ar_ye.php | 170 +- framework/i18n/data/as.php | 27 +- framework/i18n/data/as_in.php | 27 +- framework/i18n/data/asa.php | 12 +- framework/i18n/data/asa_tz.php | 12 +- framework/i18n/data/az.php | 10 +- framework/i18n/data/az_cyrl.php | 10 +- framework/i18n/data/az_cyrl_az.php | 10 +- framework/i18n/data/az_latn.php | 10 +- framework/i18n/data/az_latn_az.php | 10 +- framework/i18n/data/bas.php | 12 +- framework/i18n/data/bas_cm.php | 12 +- framework/i18n/data/be.php | 47 +- framework/i18n/data/be_by.php | 47 +- framework/i18n/data/bem.php | 42 +- framework/i18n/data/bem_zm.php | 42 +- framework/i18n/data/bez.php | 12 +- framework/i18n/data/bez_tz.php | 12 +- framework/i18n/data/bg.php | 41 +- framework/i18n/data/bg_bg.php | 41 +- framework/i18n/data/bm.php | 14 +- framework/i18n/data/bm_ml.php | 14 +- framework/i18n/data/bn.php | 77 +- framework/i18n/data/bn_bd.php | 77 +- framework/i18n/data/bn_in.php | 73 +- framework/i18n/data/bo.php | 41 +- framework/i18n/data/bo_cn.php | 41 +- framework/i18n/data/bo_in.php | 40 +- framework/i18n/data/br.php | 302 +- framework/i18n/data/br_fr.php | 302 +- framework/i18n/data/brx.php | 41 +- framework/i18n/data/brx_in.php | 41 +- framework/i18n/data/bs.php | 25 +- framework/i18n/data/byn.php | 10 +- framework/i18n/data/byn_er.php | 10 +- framework/i18n/data/ca.php | 202 +- framework/i18n/data/ca_es.php | 202 +- framework/i18n/data/cgg.php | 12 +- framework/i18n/data/cgg_ug.php | 12 +- framework/i18n/data/chr.php | 9 +- framework/i18n/data/chr_us.php | 9 +- framework/i18n/data/cs.php | 239 +- framework/i18n/data/cs_cz.php | 239 +- framework/i18n/data/cy.php | 685 +- framework/i18n/data/cy_gb.php | 685 +- framework/i18n/data/da.php | 213 +- framework/i18n/data/da_dk.php | 213 +- framework/i18n/data/dav.php | 12 +- framework/i18n/data/dav_ke.php | 12 +- framework/i18n/data/de.php | 123 +- framework/i18n/data/de_at.php | 121 +- framework/i18n/data/de_be.php | 123 +- framework/i18n/data/de_ch.php | 124 +- framework/i18n/data/de_de.php | 123 +- framework/i18n/data/de_li.php | 123 +- framework/i18n/data/de_lu.php | 123 +- framework/i18n/data/dje.php | 12 +- framework/i18n/data/dje_ne.php | 12 +- framework/i18n/data/dua.php | 11 +- framework/i18n/data/dua_cm.php | 11 +- framework/i18n/data/dyo.php | 11 +- framework/i18n/data/dyo_sn.php | 11 +- framework/i18n/data/dz.php | 780 +- framework/i18n/data/dz_bt.php | 780 +- framework/i18n/data/ebu.php | 12 +- framework/i18n/data/ebu_ke.php | 12 +- framework/i18n/data/ee.php | 213 +- framework/i18n/data/ee_gh.php | 213 +- framework/i18n/data/ee_tg.php | 213 +- framework/i18n/data/el.php | 144 +- framework/i18n/data/el_cy.php | 144 +- framework/i18n/data/el_gr.php | 144 +- framework/i18n/data/en.php | 108 +- framework/i18n/data/en_as.php | 108 +- framework/i18n/data/en_au.php | 108 +- framework/i18n/data/en_bb.php | 108 +- framework/i18n/data/en_be.php | 110 +- framework/i18n/data/en_bm.php | 108 +- framework/i18n/data/en_bw.php | 110 +- framework/i18n/data/en_bz.php | 108 +- framework/i18n/data/en_ca.php | 116 +- framework/i18n/data/en_dsrt.php | 100 +- framework/i18n/data/en_dsrt_us.php | 100 +- framework/i18n/data/en_gb.php | 104 +- framework/i18n/data/en_gu.php | 108 +- framework/i18n/data/en_gy.php | 111 +- framework/i18n/data/en_hk.php | 108 +- framework/i18n/data/en_ie.php | 110 +- framework/i18n/data/en_in.php | 110 +- framework/i18n/data/en_jm.php | 108 +- framework/i18n/data/en_mh.php | 108 +- framework/i18n/data/en_mp.php | 108 +- framework/i18n/data/en_mt.php | 112 +- framework/i18n/data/en_mu.php | 111 +- framework/i18n/data/en_na.php | 108 +- framework/i18n/data/en_nz.php | 110 +- framework/i18n/data/en_ph.php | 108 +- framework/i18n/data/en_pk.php | 114 +- framework/i18n/data/en_sg.php | 108 +- framework/i18n/data/en_tt.php | 108 +- framework/i18n/data/en_um.php | 108 +- framework/i18n/data/en_us.php | 108 +- framework/i18n/data/en_us_posix.php | 108 +- framework/i18n/data/en_vi.php | 108 +- framework/i18n/data/en_za.php | 110 +- framework/i18n/data/en_zw.php | 112 +- framework/i18n/data/eo.php | 29 +- framework/i18n/data/es.php | 122 +- framework/i18n/data/es_419.php | 122 +- framework/i18n/data/es_ar.php | 122 +- framework/i18n/data/es_bo.php | 122 +- framework/i18n/data/es_cl.php | 120 +- framework/i18n/data/es_co.php | 122 +- framework/i18n/data/es_cr.php | 122 +- framework/i18n/data/es_do.php | 122 +- framework/i18n/data/es_ec.php | 122 +- framework/i18n/data/es_es.php | 122 +- framework/i18n/data/es_gq.php | 122 +- framework/i18n/data/es_gt.php | 122 +- framework/i18n/data/es_hn.php | 122 +- framework/i18n/data/es_mx.php | 122 +- framework/i18n/data/es_ni.php | 122 +- framework/i18n/data/es_pa.php | 122 +- framework/i18n/data/es_pe.php | 122 +- framework/i18n/data/es_pr.php | 130 +- framework/i18n/data/es_py.php | 122 +- framework/i18n/data/es_sv.php | 122 +- framework/i18n/data/es_us.php | 124 +- framework/i18n/data/es_uy.php | 122 +- framework/i18n/data/es_ve.php | 124 +- framework/i18n/data/et.php | 204 +- framework/i18n/data/et_ee.php | 204 +- framework/i18n/data/eu.php | 99 +- framework/i18n/data/eu_es.php | 99 +- framework/i18n/data/ewo.php | 222 +- framework/i18n/data/ewo_cm.php | 222 +- framework/i18n/data/fa.php | 64 +- framework/i18n/data/fa_af.php | 64 +- framework/i18n/data/fa_ir.php | 64 +- framework/i18n/data/ff.php | 12 +- framework/i18n/data/ff_sn.php | 12 +- framework/i18n/data/fi.php | 167 +- framework/i18n/data/fi_fi.php | 167 +- framework/i18n/data/fil.php | 81 +- framework/i18n/data/fil_ph.php | 81 +- framework/i18n/data/fo.php | 87 +- framework/i18n/data/fo_fo.php | 87 +- framework/i18n/data/fr.php | 279 +- framework/i18n/data/fr_be.php | 277 +- framework/i18n/data/fr_bf.php | 279 +- framework/i18n/data/fr_bi.php | 279 +- framework/i18n/data/fr_bj.php | 279 +- framework/i18n/data/fr_bl.php | 279 +- framework/i18n/data/fr_ca.php | 265 +- framework/i18n/data/fr_cd.php | 280 +- framework/i18n/data/fr_cf.php | 279 +- framework/i18n/data/fr_cg.php | 279 +- framework/i18n/data/fr_ch.php | 282 +- framework/i18n/data/fr_ci.php | 279 +- framework/i18n/data/fr_cm.php | 279 +- framework/i18n/data/fr_dj.php | 280 +- framework/i18n/data/fr_fr.php | 279 +- framework/i18n/data/fr_ga.php | 279 +- framework/i18n/data/fr_gf.php | 279 +- framework/i18n/data/fr_gn.php | 279 +- framework/i18n/data/fr_gp.php | 279 +- framework/i18n/data/fr_gq.php | 279 +- framework/i18n/data/fr_km.php | 280 +- framework/i18n/data/fr_lu.php | 279 +- framework/i18n/data/fr_mc.php | 279 +- framework/i18n/data/fr_mf.php | 279 +- framework/i18n/data/fr_mg.php | 280 +- framework/i18n/data/fr_ml.php | 279 +- framework/i18n/data/fr_mq.php | 279 +- framework/i18n/data/fr_ne.php | 279 +- framework/i18n/data/fr_re.php | 279 +- framework/i18n/data/fr_rw.php | 280 +- framework/i18n/data/fr_sn.php | 279 +- framework/i18n/data/fr_td.php | 279 +- framework/i18n/data/fr_tg.php | 279 +- framework/i18n/data/fr_yt.php | 279 +- framework/i18n/data/fur.php | 12 +- framework/i18n/data/fur_it.php | 12 +- framework/i18n/data/ga.php | 52 +- framework/i18n/data/ga_ie.php | 52 +- framework/i18n/data/gd.php | 17 +- framework/i18n/data/gd_gb.php | 17 +- framework/i18n/data/gl.php | 84 +- framework/i18n/data/gl_es.php | 84 +- framework/i18n/data/gsw.php | 13 +- framework/i18n/data/gsw_ch.php | 13 +- framework/i18n/data/gu.php | 160 +- framework/i18n/data/gu_in.php | 160 +- framework/i18n/data/guz.php | 12 +- framework/i18n/data/guz_ke.php | 12 +- framework/i18n/data/gv.php | 23 +- framework/i18n/data/gv_gb.php | 23 +- framework/i18n/data/ha.php | 10 +- framework/i18n/data/ha_latn.php | 10 +- framework/i18n/data/ha_latn_gh.php | 11 +- framework/i18n/data/ha_latn_ne.php | 10 +- framework/i18n/data/ha_latn_ng.php | 10 +- framework/i18n/data/haw.php | 25 +- framework/i18n/data/haw_us.php | 25 +- framework/i18n/data/he.php | 400 +- framework/i18n/data/he_il.php | 400 +- framework/i18n/data/hi.php | 316 +- framework/i18n/data/hi_in.php | 316 +- framework/i18n/data/hr.php | 160 +- framework/i18n/data/hr_hr.php | 160 +- framework/i18n/data/hu.php | 139 +- framework/i18n/data/hu_hu.php | 139 +- framework/i18n/data/hy.php | 615 +- framework/i18n/data/hy_am.php | 615 +- framework/i18n/data/ia.php | 24 +- framework/i18n/data/id.php | 404 +- framework/i18n/data/id_id.php | 404 +- framework/i18n/data/ig.php | 25 +- framework/i18n/data/ig_ng.php | 25 +- framework/i18n/data/ii.php | 15 +- framework/i18n/data/ii_cn.php | 15 +- framework/i18n/data/is.php | 196 +- framework/i18n/data/is_is.php | 196 +- framework/i18n/data/it.php | 133 +- framework/i18n/data/it_ch.php | 134 +- framework/i18n/data/it_it.php | 133 +- framework/i18n/data/ja.php | 324 +- framework/i18n/data/ja_jp.php | 324 +- framework/i18n/data/jmc.php | 12 +- framework/i18n/data/jmc_tz.php | 12 +- framework/i18n/data/ka.php | 674 +- framework/i18n/data/ka_ge.php | 674 +- framework/i18n/data/kab.php | 12 +- framework/i18n/data/kab_dz.php | 12 +- framework/i18n/data/kam.php | 12 +- framework/i18n/data/kam_ke.php | 12 +- framework/i18n/data/kde.php | 12 +- framework/i18n/data/kde_tz.php | 12 +- framework/i18n/data/kea.php | 167 +- framework/i18n/data/kea_cv.php | 167 +- framework/i18n/data/khq.php | 12 +- framework/i18n/data/khq_ml.php | 12 +- framework/i18n/data/ki.php | 12 +- framework/i18n/data/ki_ke.php | 12 +- framework/i18n/data/kk.php | 80 +- framework/i18n/data/kk_cyrl.php | 80 +- framework/i18n/data/kk_cyrl_kz.php | 80 +- framework/i18n/data/kl.php | 130 +- framework/i18n/data/kl_gl.php | 130 +- framework/i18n/data/kln.php | 12 +- framework/i18n/data/kln_ke.php | 12 +- framework/i18n/data/km.php | 582 +- framework/i18n/data/km_kh.php | 582 +- framework/i18n/data/kn.php | 180 +- framework/i18n/data/kn_in.php | 180 +- framework/i18n/data/ko.php | 194 +- framework/i18n/data/ko_kr.php | 194 +- framework/i18n/data/kok.php | 49 +- framework/i18n/data/kok_in.php | 49 +- framework/i18n/data/ksb.php | 12 +- framework/i18n/data/ksb_tz.php | 12 +- framework/i18n/data/ksf.php | 12 +- framework/i18n/data/ksf_cm.php | 12 +- framework/i18n/data/ksh.php | 132 +- framework/i18n/data/ksh_de.php | 132 +- framework/i18n/data/kw.php | 27 +- framework/i18n/data/kw_gb.php | 27 +- framework/i18n/data/ky.php | 178 +- framework/i18n/data/ky_kg.php | 178 +- framework/i18n/data/lag.php | 12 +- framework/i18n/data/lag_tz.php | 12 +- framework/i18n/data/lg.php | 12 +- framework/i18n/data/lg_ug.php | 12 +- framework/i18n/data/ln.php | 80 +- framework/i18n/data/ln_cd.php | 80 +- framework/i18n/data/ln_cg.php | 80 +- framework/i18n/data/lo.php | 1086 +- framework/i18n/data/lo_la.php | 1086 +- framework/i18n/data/lt.php | 349 +- framework/i18n/data/lt_lt.php | 349 +- framework/i18n/data/lu.php | 13 +- framework/i18n/data/lu_cd.php | 13 +- framework/i18n/data/luo.php | 13 +- framework/i18n/data/luo_ke.php | 13 +- framework/i18n/data/luy.php | 13 +- framework/i18n/data/luy_ke.php | 13 +- framework/i18n/data/lv.php | 225 +- framework/i18n/data/lv_lv.php | 225 +- framework/i18n/data/mas.php | 12 +- framework/i18n/data/mas_ke.php | 12 +- framework/i18n/data/mas_tz.php | 12 +- framework/i18n/data/mer.php | 12 +- framework/i18n/data/mer_ke.php | 12 +- framework/i18n/data/mfe.php | 13 +- framework/i18n/data/mfe_mu.php | 13 +- framework/i18n/data/mg.php | 13 +- framework/i18n/data/mg_mg.php | 13 +- framework/i18n/data/mgh.php | 16 +- framework/i18n/data/mgh_mz.php | 16 +- framework/i18n/data/mk.php | 118 +- framework/i18n/data/mk_mk.php | 118 +- framework/i18n/data/ml.php | 893 +- framework/i18n/data/ml_in.php | 893 +- framework/i18n/data/mn.php | 555 +- framework/i18n/data/mn_cyrl.php | 555 +- framework/i18n/data/mn_cyrl_mn.php | 555 +- framework/i18n/data/mr.php | 90 +- framework/i18n/data/mr_in.php | 90 +- framework/i18n/data/ms.php | 131 +- framework/i18n/data/mt.php | 25 +- framework/i18n/data/mt_mt.php | 25 +- framework/i18n/data/mua.php | 12 +- framework/i18n/data/mua_cm.php | 12 +- framework/i18n/data/my.php | 11 +- framework/i18n/data/my_mm.php | 11 +- framework/i18n/data/naq.php | 12 +- framework/i18n/data/naq_na.php | 12 +- framework/i18n/data/nb.php | 100 +- framework/i18n/data/nb_no.php | 100 +- framework/i18n/data/nd.php | 12 +- framework/i18n/data/nd_zw.php | 12 +- framework/i18n/data/ne.php | 226 +- framework/i18n/data/ne_in.php | 224 +- framework/i18n/data/ne_np.php | 226 +- framework/i18n/data/nl.php | 197 +- framework/i18n/data/nl_aw.php | 197 +- framework/i18n/data/nl_be.php | 199 +- framework/i18n/data/nl_cw.php | 197 +- framework/i18n/data/nl_nl.php | 197 +- framework/i18n/data/nl_sx.php | 197 +- framework/i18n/data/nmg.php | 13 +- framework/i18n/data/nmg_cm.php | 13 +- framework/i18n/data/nn.php | 24 +- framework/i18n/data/nn_no.php | 24 +- framework/i18n/data/nr.php | 27 +- framework/i18n/data/nr_za.php | 27 +- framework/i18n/data/nso.php | 27 +- framework/i18n/data/nso_za.php | 27 +- framework/i18n/data/nus.php | 11 +- framework/i18n/data/nus_sd.php | 11 +- framework/i18n/data/nyn.php | 12 +- framework/i18n/data/nyn_ug.php | 12 +- framework/i18n/data/om.php | 9 +- framework/i18n/data/om_et.php | 9 +- framework/i18n/data/om_ke.php | 9 +- framework/i18n/data/or.php | 35 +- framework/i18n/data/or_in.php | 35 +- framework/i18n/data/pa.php | 39 +- framework/i18n/data/pa_arab.php | 41 +- framework/i18n/data/pa_arab_pk.php | 41 +- framework/i18n/data/pa_guru.php | 39 +- framework/i18n/data/pa_guru_in.php | 39 +- framework/i18n/data/pl.php | 165 +- framework/i18n/data/pl_pl.php | 165 +- framework/i18n/data/ps.php | 63 +- framework/i18n/data/ps_af.php | 63 +- framework/i18n/data/pt.php | 140 +- framework/i18n/data/pt_ao.php | 140 +- framework/i18n/data/pt_br.php | 140 +- framework/i18n/data/pt_gw.php | 140 +- framework/i18n/data/pt_mz.php | 140 +- framework/i18n/data/pt_pt.php | 325 +- framework/i18n/data/pt_st.php | 140 +- framework/i18n/data/rm.php | 19 +- framework/i18n/data/rm_ch.php | 19 +- framework/i18n/data/rn.php | 26 +- framework/i18n/data/rn_bi.php | 26 +- framework/i18n/data/ro.php | 373 +- framework/i18n/data/ro_md.php | 373 +- framework/i18n/data/ro_ro.php | 373 +- framework/i18n/data/rof.php | 12 +- framework/i18n/data/rof_tz.php | 12 +- framework/i18n/data/root.php | 103 +- framework/i18n/data/ru.php | 169 +- framework/i18n/data/ru_md.php | 169 +- framework/i18n/data/ru_ru.php | 169 +- framework/i18n/data/ru_ua.php | 165 +- framework/i18n/data/rw.php | 23 +- framework/i18n/data/rw_rw.php | 23 +- framework/i18n/data/rwk.php | 12 +- framework/i18n/data/rwk_tz.php | 12 +- framework/i18n/data/sah.php | 10 +- framework/i18n/data/sah_ru.php | 10 +- framework/i18n/data/saq.php | 12 +- framework/i18n/data/saq_ke.php | 12 +- framework/i18n/data/sbp.php | 12 +- framework/i18n/data/sbp_tz.php | 12 +- framework/i18n/data/se.php | 40 +- framework/i18n/data/se_fi.php | 40 +- framework/i18n/data/se_no.php | 40 +- framework/i18n/data/seh.php | 12 +- framework/i18n/data/seh_mz.php | 12 +- framework/i18n/data/ses.php | 12 +- framework/i18n/data/ses_ml.php | 12 +- framework/i18n/data/sg.php | 13 +- framework/i18n/data/sg_cf.php | 13 +- framework/i18n/data/shi.php | 682 +- framework/i18n/data/shi_latn.php | 30 +- framework/i18n/data/shi_latn_ma.php | 30 +- framework/i18n/data/shi_tfng.php | 30 +- framework/i18n/data/shi_tfng_ma.php | 30 +- framework/i18n/data/si.php | 242 +- framework/i18n/data/si_lk.php | 242 +- framework/i18n/data/sk.php | 403 +- framework/i18n/data/sk_sk.php | 403 +- framework/i18n/data/sl.php | 44 +- framework/i18n/data/sl_si.php | 44 +- framework/i18n/data/sn.php | 12 +- framework/i18n/data/sn_zw.php | 12 +- framework/i18n/data/so.php | 12 +- framework/i18n/data/so_dj.php | 12 +- framework/i18n/data/so_et.php | 12 +- framework/i18n/data/so_ke.php | 12 +- framework/i18n/data/so_so.php | 12 +- framework/i18n/data/sq.php | 15 +- framework/i18n/data/sq_al.php | 15 +- framework/i18n/data/sr.php | 124 +- framework/i18n/data/sr_cyrl.php | 124 +- framework/i18n/data/sr_cyrl_ba.php | 128 +- framework/i18n/data/sr_cyrl_me.php | 124 +- framework/i18n/data/sr_cyrl_rs.php | 124 +- framework/i18n/data/sr_latn.php | 314 +- framework/i18n/data/sr_latn_ba.php | 314 +- framework/i18n/data/sr_latn_me.php | 316 +- framework/i18n/data/sr_latn_rs.php | 314 +- framework/i18n/data/ss.php | 27 +- framework/i18n/data/ss_sz.php | 27 +- framework/i18n/data/ss_za.php | 27 +- framework/i18n/data/ssy.php | 9 +- framework/i18n/data/ssy_er.php | 9 +- framework/i18n/data/st.php | 27 +- framework/i18n/data/st_ls.php | 27 +- framework/i18n/data/st_za.php | 27 +- framework/i18n/data/sv.php | 218 +- framework/i18n/data/sv_fi.php | 224 +- framework/i18n/data/sv_se.php | 218 +- framework/i18n/data/sw.php | 186 +- framework/i18n/data/sw_ke.php | 184 +- framework/i18n/data/sw_tz.php | 186 +- framework/i18n/data/swc.php | 13 +- framework/i18n/data/swc_cd.php | 13 +- framework/i18n/data/ta.php | 96 +- framework/i18n/data/ta_in.php | 96 +- framework/i18n/data/ta_lk.php | 96 +- framework/i18n/data/te.php | 155 +- framework/i18n/data/te_in.php | 155 +- framework/i18n/data/teo.php | 12 +- framework/i18n/data/teo_ke.php | 12 +- framework/i18n/data/teo_ug.php | 12 +- framework/i18n/data/tg.php | 25 +- framework/i18n/data/tg_cyrl.php | 25 +- framework/i18n/data/tg_cyrl_tj.php | 25 +- framework/i18n/data/th.php | 245 +- framework/i18n/data/th_th.php | 245 +- framework/i18n/data/ti.php | 29 +- framework/i18n/data/ti_er.php | 29 +- framework/i18n/data/ti_et.php | 29 +- framework/i18n/data/tig.php | 10 +- framework/i18n/data/tig_er.php | 10 +- framework/i18n/data/tn.php | 27 +- framework/i18n/data/tn_za.php | 27 +- framework/i18n/data/to.php | 212 +- framework/i18n/data/to_to.php | 212 +- framework/i18n/data/tr.php | 207 +- framework/i18n/data/tr_tr.php | 207 +- framework/i18n/data/ts.php | 27 +- framework/i18n/data/ts_za.php | 27 +- framework/i18n/data/twq.php | 16 +- framework/i18n/data/twq_ne.php | 16 +- framework/i18n/data/tzm.php | 12 +- framework/i18n/data/tzm_latn.php | 12 +- framework/i18n/data/tzm_latn_ma.php | 12 +- framework/i18n/data/ug.php | 1293 +- framework/i18n/data/ug_arab.php | 1293 +- framework/i18n/data/ug_arab_cn.php | 1293 +- framework/i18n/data/uk.php | 102 +- framework/i18n/data/uk_ua.php | 102 +- framework/i18n/data/ur.php | 248 +- framework/i18n/data/ur_in.php | 248 +- framework/i18n/data/ur_pk.php | 248 +- framework/i18n/data/uz.php | 9 +- framework/i18n/data/uz_arab.php | 17 +- framework/i18n/data/uz_arab_af.php | 17 +- framework/i18n/data/uz_cyrl.php | 9 +- framework/i18n/data/uz_cyrl_uz.php | 9 +- framework/i18n/data/uz_latn.php | 19 +- framework/i18n/data/uz_latn_uz.php | 19 +- framework/i18n/data/vai.php | 64 +- framework/i18n/data/vai_latn.php | 64 +- framework/i18n/data/vai_latn_lr.php | 64 +- framework/i18n/data/vai_vaii.php | 64 +- framework/i18n/data/vai_vaii_lr.php | 64 +- framework/i18n/data/ve.php | 27 +- framework/i18n/data/ve_za.php | 27 +- framework/i18n/data/vi.php | 875 +- framework/i18n/data/vi_vn.php | 875 +- framework/i18n/data/vun.php | 12 +- framework/i18n/data/vun_tz.php | 12 +- framework/i18n/data/wae.php | 11 +- framework/i18n/data/wae_ch.php | 11 +- framework/i18n/data/wal.php | 24 +- framework/i18n/data/wal_et.php | 24 +- framework/i18n/data/xh.php | 27 +- framework/i18n/data/xh_za.php | 27 +- framework/i18n/data/xog.php | 12 +- framework/i18n/data/xog_ug.php | 12 +- framework/i18n/data/yav.php | 12 +- framework/i18n/data/yav_cm.php | 12 +- framework/i18n/data/yo.php | 27 +- framework/i18n/data/yo_ng.php | 27 +- framework/i18n/data/zh.php | 472 +- framework/i18n/data/zh_hans.php | 472 +- framework/i18n/data/zh_hans_cn.php | 472 +- framework/i18n/data/zh_hans_hk.php | 318 +- framework/i18n/data/zh_hans_mo.php | 321 +- framework/i18n/data/zh_hans_sg.php | 325 +- framework/i18n/data/zh_hant.php | 238 +- framework/i18n/data/zh_hant_hk.php | 234 +- framework/i18n/data/zh_hant_mo.php | 237 +- framework/i18n/data/zh_hant_tw.php | 238 +- framework/i18n/data/zu.php | 80 +- framework/i18n/data/zu_za.php | 80 +- framework/logging/CFileLogRoute.php | 2 + framework/logging/CLogRouter.php | 10 +- framework/messages/ar/zii.php | 19 +- framework/messages/ca/yii.php | 2 +- framework/messages/config.php | 2 +- framework/messages/de/yii.php | 15 +- framework/messages/fa_ir/yii.php | 6 +- framework/messages/fr/yii.php | 84 +- framework/messages/it/yii.php | 3 +- framework/messages/ja/yii.php | 3 +- framework/messages/lv/yii.php | 4 +- framework/messages/nl/yii.php | 17 +- framework/messages/nl/zii.php | 4 +- framework/messages/pl/yii.php | 8 +- framework/messages/sk/yii.php | 11 +- framework/messages/uk/yii.php | 10 +- framework/test/CTestCase.php | 18 +- framework/utils/CDateTimeParser.php | 2 + framework/utils/CFileHelper.php | 94 +- framework/utils/CFormatter.php | 5 +- framework/utils/CLocalizedFormatter.php | 2 +- framework/utils/CPasswordHelper.php | 8 +- framework/utils/mimeTypes.php | 945 +- framework/validators/CEmailValidator.php | 10 +- framework/validators/CFileValidator.php | 70 +- framework/validators/CRangeValidator.php | 2 +- framework/validators/CValidator.php | 14 +- framework/vendors/README.html | 6 +- .../htmlpurifier/HTMLPurifier.standalone.php | 11588 +++++++++---- .../HTMLPurifier/ConfigSchema/Interchange.php | 11 +- .../ConfigSchema/InterchangeBuilder.php | 90 +- .../HTMLPurifier/ConfigSchema/Validator.php | 90 +- .../ConfigSchema/ValidatorAtom.php | 108 +- .../HTMLPurifier/ConfigSchema/schema.ser | Bin 14880 -> 15000 bytes .../schema/Core.EscapeInvalidChildren.txt | 6 +- .../schema/URI.MungeSecretKey.txt | 2 +- .../HTMLPurifier/Language/messages/en.php | 90 +- .../standalone/HTMLPurifier/Printer.php | 134 +- .../HTMLPurifier/Printer/CSSDefinition.php | 12 +- .../HTMLPurifier/Printer/ConfigForm.php | 257 +- .../HTMLPurifier/Printer/HTMLDefinition.php | 214 +- framework/vendors/jquery/LICENSE.txt | 2 +- framework/vendors/jqueryui/MIT-LICENSE.txt | 30 +- framework/views/ca/error.php | 0 framework/views/ca/error400.php | 0 framework/views/ca/error403.php | 0 framework/views/ca/error404.php | 0 framework/views/ca/error500.php | 0 framework/views/ca/error503.php | 0 framework/views/ca/log-firebug.php | 0 framework/views/ca/log.php | 0 .../views/ca/profile-callstack-firebug.php | 0 framework/views/ca/profile-callstack.php | 0 .../views/ca/profile-summary-firebug.php | 0 framework/views/ca/profile-summary.php | 0 framework/views/error403.php | 4 +- framework/views/ja/error403.php | 4 +- framework/web/CAssetManager.php | 10 +- framework/web/CBaseController.php | 12 +- framework/web/CClientScript.php | 26 +- framework/web/CDbHttpSession.php | 5 +- framework/web/CHttpRequest.php | 128 +- framework/web/CHttpSession.php | 3 +- framework/web/CSort.php | 6 +- framework/web/CUploadedFile.php | 5 +- framework/web/CUrlManager.php | 1 + framework/web/CWebApplication.php | 2 +- framework/web/actions/CAction.php | 5 +- framework/web/actions/CInlineAction.php | 6 +- framework/web/actions/CViewAction.php | 2 +- framework/web/auth/CAuthManager.php | 2 +- framework/web/auth/CDbAuthManager.php | 2 +- framework/web/auth/CPhpAuthManager.php | 4 +- framework/web/filters/CHttpCacheFilter.php | 3 +- framework/web/form/CForm.php | 10 +- framework/web/form/CFormElement.php | 21 + framework/web/form/CFormInputElement.php | 20 +- framework/web/helpers/CHtml.php | 223 +- framework/web/helpers/CJSON.php | 10 +- .../web/js/source/jquery.autocomplete.js | 7 +- framework/web/js/source/jquery.bgiframe.js | 100 +- framework/web/js/source/jquery.js | 14186 ++++++++-------- framework/web/js/source/jquery.min.js | 6 +- framework/web/js/source/jquery.multifile.js | 23 +- .../web/js/source/jquery.treeview.edit.js | 4 +- .../web/js/source/jquery.yiiactiveform.js | 12 +- .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 86 -> 212 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 75 -> 208 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 144 -> 335 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 99 -> 207 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 142 -> 262 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 137 -> 262 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 140 -> 332 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 86 -> 280 bytes .../base/images/ui-icons_222222_256x240.png | Bin 3800 -> 6922 bytes .../base/images/ui-icons_2e83ff_256x240.png | Bin 3800 -> 4549 bytes .../base/images/ui-icons_454545_256x240.png | Bin 3800 -> 6992 bytes .../base/images/ui-icons_888888_256x240.png | Bin 3800 -> 6999 bytes .../base/images/ui-icons_cd0a0a_256x240.png | Bin 3800 -> 4549 bytes .../web/js/source/jui/css/base/jquery-ui.css | 13 +- .../jui/css/base/jquery.ui.accordion.css | 5 - .../jui/css/base/jquery.ui.autocomplete.css | 5 - .../source/jui/css/base/jquery.ui.button.css | 5 - .../js/source/jui/css/base/jquery.ui.core.css | 5 - .../jui/css/base/jquery.ui.datepicker.css | 5 - .../source/jui/css/base/jquery.ui.dialog.css | 5 - .../js/source/jui/css/base/jquery.ui.menu.css | 5 - .../jui/css/base/jquery.ui.progressbar.css | 5 - .../jui/css/base/jquery.ui.resizable.css | 5 - .../jui/css/base/jquery.ui.selectable.css | 5 - .../source/jui/css/base/jquery.ui.slider.css | 5 - .../source/jui/css/base/jquery.ui.spinner.css | 5 - .../js/source/jui/css/base/jquery.ui.tabs.css | 5 - .../source/jui/css/base/jquery.ui.theme.css | 5 - .../source/jui/css/base/jquery.ui.tooltip.css | 5 - .../js/source/jui/js/jquery-ui-i18n.min.js | 10 +- .../web/js/source/jui/js/jquery-ui.min.js | 15 +- framework/web/services/CWebService.php | 68 +- framework/web/services/CWsdlGenerator.php | 251 +- framework/web/widgets/CActiveForm.php | 85 +- framework/web/widgets/CHtmlPurifier.php | 2 +- framework/web/widgets/captcha/CCaptcha.php | 3 + .../web/widgets/captcha/CCaptchaAction.php | 8 +- framework/web/widgets/pagers/CLinkPager.php | 34 +- framework/yiic | 0 framework/yiilite.php | 321 +- framework/zii/widgets/CBaseListView.php | 20 +- framework/zii/widgets/CBreadcrumbs.php | 6 +- framework/zii/widgets/CDetailView.php | 4 +- framework/zii/widgets/CListView.php | 4 +- framework/zii/widgets/CMenu.php | 9 +- .../assets/gridview/jquery.yiigridview.js | 32 +- .../assets/listview/jquery.yiilistview.js | 32 +- framework/zii/widgets/grid/CButtonColumn.php | 12 +- .../zii/widgets/grid/CCheckBoxColumn.php | 34 +- framework/zii/widgets/grid/CDataColumn.php | 41 +- framework/zii/widgets/grid/CGridColumn.php | 71 +- framework/zii/widgets/grid/CGridView.php | 31 +- framework/zii/widgets/grid/CLinkColumn.php | 12 +- framework/zii/widgets/jui/CJuiAccordion.php | 2 +- framework/zii/widgets/jui/CJuiTabs.php | 13 +- 752 files changed, 75306 insertions(+), 32854 deletions(-) delete mode 100644 framework/CHANGELOG delete mode 100644 framework/LICENSE delete mode 100644 framework/README delete mode 100644 framework/UPGRADE mode change 100644 => 100755 framework/cli/views/webapp/protected/yiic mode change 100644 => 100755 framework/views/ca/error.php mode change 100644 => 100755 framework/views/ca/error400.php mode change 100644 => 100755 framework/views/ca/error403.php mode change 100644 => 100755 framework/views/ca/error404.php mode change 100644 => 100755 framework/views/ca/error500.php mode change 100644 => 100755 framework/views/ca/error503.php mode change 100644 => 100755 framework/views/ca/log-firebug.php mode change 100644 => 100755 framework/views/ca/log.php mode change 100644 => 100755 framework/views/ca/profile-callstack-firebug.php mode change 100644 => 100755 framework/views/ca/profile-callstack.php mode change 100644 => 100755 framework/views/ca/profile-summary-firebug.php mode change 100644 => 100755 framework/views/ca/profile-summary.php delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.accordion.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.autocomplete.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.button.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.core.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.datepicker.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.dialog.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.menu.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.progressbar.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.resizable.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.selectable.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.slider.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.spinner.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.tabs.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.theme.css delete mode 100644 framework/web/js/source/jui/css/base/jquery.ui.tooltip.css mode change 100644 => 100755 framework/yiic diff --git a/framework/CHANGELOG b/framework/CHANGELOG deleted file mode 100644 index 1a5b0c36fb0..00000000000 --- a/framework/CHANGELOG +++ /dev/null @@ -1,1660 +0,0 @@ - Yii Framework Change Log - ======================== - -Version 1.1.15 June 29, 2014 ----------------------------- -- Bug (CVE-2014-4672): CDetailView may be exploited to allow executing arbitrary PHP script on the server (cebe, qiangxue) - -Version 1.1.14 August 11, 2013 ------------------------------- -- Bug: There was unnecessary echo in CRUD views generated by Gii (samdark) -- Bug: CJavaScript::encode was formatting floats in a wrong way during encoding (samdark) -- Bug: Fixed minLength and maxLength range check in CCaptchaAction::generateVerifyCode so values are now always stay in bounds (samdark) -- Bug #101: CActiveFinder::buildJoinTree() no longer uses 'false' for 'select' value (klimov-paul) -- Bug #135: Fixed wrong CActiveRecord rows count with having (klimov-paul) -- Bug #139: Fixed Active Record lazy load through relation with condition (klimov-paul) -- Bug #150: Fixed CWidget was not switching between view paths when using themes (antoncpu) -- Bug #159: CUploadedFile::getInstancesByName() has been fixed allowing correct fetching files, which name is a part of other file name (klimov-paul) -- Bug #196: CActiveForm: models list whose errors should be displayed in error summary is now customizable when using AJAX validation (resurtm) -- Bug #662: Fixed incorrect Active Record lazy loading of relation through BELONGS_TO relation (klimov-paul) -- Bug #1464: Fixed transparent background for ImageMagick in CCaptchaAction (manuel-84, cebe) -- Bug #1669: CNumberValidator used to add wrong error messages in case non-numeric values being validated (resurtm) -- Bug #1692: CWebUser::renewCookie() and CWebUser::restoreFromCookie() now make use of the identityCookie options (f10i) -- Bug #1693: Fixed log file will rotate twice when high performance (monque) -- Bug #1724: Allow CClientScript registering scripts and script files with the HTML options (klimov-paul) -- Bug #1732: CWebLogRoute and CProfileLogRoute with enabled $showInFireBug: fixed bug related to JS `console` object in MSIE 8 and 9 (resurtm) -- Bug #1763: CSqlDataProvider was appending another ORDER BY string to an existing ORDER BY statement when using fieldname with dot (szako) -- Bug #1827: Gii wasn't properly handling table name with the schema part for PostgreSQL (resurtm) -- Bug #1895: Fixed erroneous language attributes in french views (located at `framework\views\fr`) (Ragazzo) -- Bug #1909: CGridView::$filterSelector now prevents default action after event has completed by returning false result in event handler function (matih) -- Bug #1911: MigrateCommand does not rely on cached value about migration table existance anymore as this info could be outdated in testing enviroment (cebe) -- Bug #1915: CDataProviderIterator: fixed init in case of disabled pagination (antoncpu) -- Bug #1916: CMssqlSchema::findColumns() issues an "invalid object name" error (resurtm) -- Bug #1924: CLogFilter::$dumper added: this property can be used to get around circular reference issue when using standard `var_export` dumper by changing it to `print_r` (resurtm) -- Bug #1933: fixed using "multiple" parameter with a value of false in CHtml::activeDropDownList, CHtml::ListBox and CHtml::DropDownList (adminnu) -- Bug #1941: yiiactiveform.js form reset now uses CHtml::errorCss instead of a hardcoded value (mdomba) -- Bug #1942: CActiveForm client/ajax validation will now remove error class from server side validation (mdomba) -- Bug #1945: Reference to undefined variable $column in CDbMigration::dropPrimaryKey (paystey) -- Bug #1955: Some validators used to cause warnings or errors in case non-scalar array typed values being checked (resurtm) -- Bug #1957: Add primary key support for MySQL schema (paystey) -- Bug #1984: CDbMigration: fix of undeclared variable usage in debug information in dropPrimaryKey (papulovskiy) -- Bug #1990: CDateFormatter::formatWeekInMonth(): incorrect result for a week which was last in a previous year and first in a next year simultaneously (resurtm) -- Bug #1996: Using yiic help for commands with parameters with array as default value resulted in PHP error with latest PHP versions (dInGd0nG, samdark) -- Bug #1997: Cache key in CGettextMessageSource::loadMessages wasn't specific enough (odevyatkov) -- Bug #2023: CHttpRequest::stripSlashes() now modifies array keys as well (etienneq) -- Bug #2030: Fixed problem with MySQL 4.x: Undefined Index: Comment in CMysqlSchema (cebe) -- Bug #2048: AR now uses alias from CActiveRecord::getTableAlias instead of always using default "t" (s-larionov) -- Bug #2049: CStatElement relation with join option throw exception when key-field present on joined table (Yiivgeny) -- Bug #2078: Fixed problem with "undefined" parameter in query string when using CListView or CGridView with enableHistory (Parpaing) -- Bug #2086: Fixed .hgignore rule for assets folder (GeXu3, Koduc) -- Bug #2087: CLocale: getLocaleDisplayName() was only returning the language display name, not the full locale display name (brandonkelly) -- Bug #2112: Fixed broken yiic shell CRUD command (mbischof) -- Bug #2121: CMssqlSchema::resetSequence() incorrectly resets sequence (resurtm, joewoodhouse) -- Bug #2122: CActiveRecord, lazy load: 'params' from relations used in 'through' option were not applied to the final SQL statement (resurtm) -- Bug #2123: Fixed error in plural rules handling if locale has no plural rules defined (cebe, stepanselyuk) -- Bug #2146: CEmailValidator fix when fsockopen() can output uncatched error 'Connection refused (61)' (armab) -- Bug #2159: Fixed SQL syntax for delete command with join in MySQL (serebrov) -- Bug #2184: CDbHttpSession now supports MS SQL Server BLOB data type (cheuschober, resurtm) -- Bug #2201: Cannot use "having" with bound params in CActiveRecord::count() (ivokund) -- Bug #2216: CDbCommandBuilder::createInCondition() has been updated, allowing to pass array of values with mixed keys for the single type column (klimov-paul) -- Bug #2223: CActiveForm::error does not respect CHtml::$errorMessageCss (ivokund) -- Bug #2239: Fixed CHtml::refresh() method to use proper syntax (mdomba) -- Bug #2241: COciSchema::resetSequence() now works the same way as the same methods for the other RDMBSes. PHPDocs of the CMssqlSchema::resetSequence(), CMysqlSchema::resetSequence(), CPgsqlSchema::resetSequence(), COciSchema::resetSequence() and CSqliteSchema::resetSequence() methods have been adjusted to fit their real functionality (resurtm) -- Bug #2244: MessageCommand has been updated, allowing to merge string with value '0' correctly (klimov-paul) -- Bug #2258: CJuiSliderInput didn't support string typed 'range' option (bookin) -- Bug #2283: Gii Model Generator's tooltips are not working and always invisible (resurtm) -- Bug #2289: CDbCacheDependency with reuseDependentData did not invalidate cache when getting cache across different requests (marcovtwout) -- Bug #2299: CMssqlSchema: findTableNames(), getTables() and getTableNames() methods used to prepend schema prefix to the table names twice (resurtm) -- Bug #2311: Fixed SQlite default value for timestamp CURRENT_TIMESTAMP (zeeke) -- Bug #2321: CGettextPoFile is now able to parse multiline msgid and msgstr declarations (resurtm) -- Bug #2325: Fixed UTF-8 troubles in CDateTimeParser (error in parsing chinese and thai dates) (s-larionov) -- Bug #2336: PostgreSQL: CDbCommandBuilder used `NULL` instead of `DEFAULT` as default value for the primary keys of serial type (resurtm) -- Bug #2368: Reset error CSS for ':input', which includes SELECT elements (blueyed) -- Bug #2398: Fixed 'Undefined variable: results' E_NOTICE at CProfileLogRoute (klimov-paul) -- Bug #2402: Fixed clientValidation incorrectly rendered as HTML attribute, when used in CActiveForm::error() (mdomba) -- Bug #2406: CUrlManager::$urlRuleClass now supports path alias value (as it was described in its PHPDoc before this fix) (resurtm) -- Bug #2423: Fixed CHtml::button() enforces "value" attribute for the image buttons (klimov-paul) -- Bug #2426: CDbCriteria::__wakeup() used to issue an error in case SQL containing fields were arrays and criteria parameters were specified (resurtm) -- Bug #2438: CViewAction now checks if requested view is a string to not fail when array was given (cebe) -- Bug #2449: CSqlDataProvider causes an error when CDbCommand with enabled PDO::FETCH_OBJ mode used as SQL source (resurtm) -- Bug #2454: Fix CMysqlColumnSchema::extractLimit for ENUM values containing comma (blueyed) -- Bug #2491: Prevent SQL exception being thrown when inserting a row into the session table whilst regenerating the session ID (mynameiszanders) -- Bug #2502: Fix match controller in access rule, match uniqueId instead id (slavcodev) -- Bug #2508: Fix CHtml::activeLabel() to resolve attribute input name for tabular input with custom 'for' (klimov-paul) -- Bug #2516: Fixed the bug that some $.fn.yiiGridView methods were not working always if a custom CGridView::template was used (buakos) -- Bug #2524: Fixed incorrect HTTPS detection (resurtm) -- Bug #2551: CWebUser::loginRequired() AJAX response now properly sends 403 (creocoder) -- Bug #2554: Fixed CRangeValidator when allowEmpty is false (samdark, creocoder) -- Bug #2565: CCaptchaAction in ImageMagick mode used to issue an exception in case $backColor or $foreColor have had leading zeros (resurtm) -- Bug #2581: Fixed the bug with empty ajaxVar in jquery.yiilistview.js and jquery.yiigridview.js (seregagl) -- Bug #2602: CUrlValidator and CEmailValidator now works correctly with display_errors = on and validateIDN = true (creocoder) -- Bug #2662: CLocale::getTerritory() used to return null value even for proper input values, bug fix #1622 made in 1.1.13 has been reverted (resurtm) -- Bug #2632: Fixed inability import non-build aliases by config on some case (Yiivgeny) -- Bug #2651: CHttpSession was using hardcoded GC probability/divisor values (marcovtwout, cebe, samdark) -- Enh: Better CFileLogRoute performance (Qiang, samdark) -- Enh: Refactored CHttpRequest::getDelete and CHttpRequest::getPut not to use _restParams directly (samdark) -- Enh #100: CLogFilter::$logVars can now be array of arrays intended for designating particular items of the $GLOBALS (resurtm, tomtomsen) -- Enh #129: Proper support of namespaced models in forms (LastDragon-ru, Ekstazi, pgaultier) -- Enh #169: Allow to set AJAX request type for CListView and CGridView (klimov-paul) -- Enh #289: Gii module could be submodule of an another module (resurtm) -- Enh #315: COciSchema::checkIntegrity() method added: allows to toggle integrity check (resurtm) -- Enh #755: Allow to get currently running command from CConsoleApplication (klimov-paul) -- Enh #1065: CJuiSliderInput now supports ranged slider when using it without model (resurtm) -- Enh #1142: CSecurityManager::computeHMAC() has been made public (resurtm) -- Enh #1353: Added onBeforeCount event to CActiveRecord (jakob-stoeck) -- Enh #1391: CDetailView: callables (including anonymous functions for PHP 5.3+) could be used as value generators of the attributes (resurtm) -- Enh #1447: CSqliteSchema: added enabling/disabling integrity check for sqlite (gleb-sternharz, resurtm) -- Enh #1589: Added HTTP range responses support to CHttpRequest::sendFile (Ragazzo, samdark) -- Enh #1604: Added method CDbCommandBuilder::createMultipleInsertCommand() to support multiple insertion (klimov-paul) -- Enh #1725: Added CFileHelper::removeDirectory() static method (resurtm) -- Enh #1743: Added CActiveForm::searchField() and CHtml::activeSearchField() to create HTML input field of type SEARCH (njasm) -- Enh #1794: Added ability to change widget ID via $htmlOptions['id'] array item in: CTabView, CBaseListView, CListView, CGridView, CDetailView, CMenu, and CPortlet (umrs) -- Enh #1796: Separate count criteria has been added to the CActiveDataProvider, it's useful for the counting queries simplification (resurtm) -- Enh #1818: Created a CLocalizedFormatter application component that allows formatting values according to current locale (cebe) -- Enh #1842: Added support for MySQL BIT(M) data type default values (migelsabre, cebe) -- Enh #1847: Added COutputCache::varyByLanguage to generate separate cache for different languages (Obramko) -- Enh #1863: Added CActiveFinder::getModel, added CActiveRecord::getActiveFinder, CExistValidator::getModel, CUniqueValidator::getModel, CActiveDataProvider::getModel, CSort::getModel (denisarius, samdark) -- Enh #1928: Gii is now able to use table columns' comments as the attribute labels of a new generated model (resurtm, tlikai) -- Enh #1948: Tidy up and improve html5 input support in CHtml and CActiveForm (phpnode) -- Enh #1977: CFormatter::normalizeDateValue() now is protected instead of private to enable child classes to override it (etienneq) -- Enh #2003: Gii now allows namespaced base classes to be defined in generators (etienneq) -- Enh #2038: CFormatter::formatNtext() method can replace newlines with `

` not just with `
` as it was before (resurtm) -- Enh #2053: CPasswordHelper has been refactored, CSecurityManager has been enhanced and is now able to generate cryptographically strong random bytes and strings (resurtm, tom--, ekerazha, samdark) -- Enh #2062: CWsdlGenerator now supports soap indicators (sequence, choice), injecting of custom WSDL string block and generation of human-friendly documentation for complex types. Added unit test. (lubosdz) -- Enh #2090: Allow passing array of columns to CDbSchema::addPrimaryKey() (paystey) -- Enh #2096: CAPTCHA: non-free Duality.ttf font replaced by open/free SpicyRice.ttf (licensed under SIL OFL v1.1) (resurtm) -- Enh #2131: Added Accept header parsing to CHttpRequest to give an array of accepted types in order of preference (Rupert-RR) -- Enh #2135: MessageCommand can now handles Yii::t() messages with files in subfolders (firsyura) -- Enh #2205: CActiveForm::error() now depends on CHtml::$errorContainerTag (malyshev) -- Enh #2213: Added comment with hint on ajax validaton which may lead to duplicate entries in the database to gii form template (elmig, cebe) -- Enh #2217: Support of the empty option for CHtml::radioButtonList() has been introduced (resurtm) -- Enh #2254: CForm::$showErrors property has been added, it controls whether error elements of the form attributes should be rendered (resurtm) -- Enh #2275: Added primary log rotation by copy and truncate to CFileLogRoute (bdstevens) -- Enh #2415: Cancel current ajax request before create a new one in CGridView and CListView (gusnips) -- Enh #2416: Avoid instantiating HTMLPurifier on each CHtmlPurifier::purify() call. Allow to pass array as argument of CHtmlPurifier::purify() (twisted1919) -- Enh #2435: CFileCache entry expiration time could now be embedded into the cache file instead of changing file's modification time to be in future (resurtm) -- Enh #2459: Absolute session timeout in CWebUser (ivokund) -- Enh #2494: Allow to configure CBaseListView emptyText container tag name (ifdattic) -- Enh #2529: Silenced all chmod calls to prevent "chmod() operation not allowed" error on NTFS (samdark) -- Enh #2602: CEmailValidator and CUrlValidator now uses native PHP `idn` extension in case it is available (`idn_to_ascii` and `idn_ to_ utf8` functions) and Net_IDNA2 otherwise (resurtm, creocoder) -- Enh #2642: Support third party autoloaders when importing classes via Yii::import() (phpnode) -- Chg: Upgraded HTMLPurifier to v4.5.0 (samdark) -- Chg #645: CDbConnection now throws CDbException when failed to open DB connection instead of failing with a warning (kidol, eirikhm, samdark, cebe) -- Chg #895: Add second argument $params to client validation function (slavcodev) -- Chg #1891: Changed order of methods in models generated by Gii and yiic, added better description of search method (hijarian, samdark) -- Chg #2069: Upgraded jQuery BBQ Plugin to 1.4pre to fix jQuery compatibility problems (samdark) -- Chg #2183: Vendors: phlymail's Net_IDNA was replaced by PEAR Net_IDNA2 (resurtm, DaSourcerer) -- Chg #2187: Vendors: punycode.js updated from 1.1.1 (June 27, 2012) to 1.2.0 (October 10, 2012) (resurtm) -- Chg #2461: Upgraded jquery star rating to 4.11 (samdark) -- Chg #2531: Upgraded jquery masked input to 1.3.1 (samdark) -- New: Added CRedisCache which uses redis key value store as cache backend (cebe, maxlun86) -- New #575: Yii registering at Packagist, added composer info file (schmunk42) -- New #1785: Added CPasswordHelper (tom--) -- New #2178: Added Catalan Translation (ArnauAregall) -- New #2370: New template placeholders for CHtml::radioButtonList() and CHtml::checkBoxList() (creocoder) -- New #2530: Added 'through' option to CActiveRecord::BELONGS_TO relation (creocoder) - -Version 1.1.13 December 30, 2012 --------------------------------- -- Bug #93: Criteria modification in CActiveRecord::beforeFind() did not apply when record was loaded in relational context. See UPGRADE instructions for details on behavior change. (cebe) -- Bug #109: formatNumber() now uses number_format() instead of round(), because of round() error in IEEE754 accuracy limitations (SonkoDmitry) -- Bug #110: MSSQL: fixed empty $primaryKey value after saving CActiveRecord model (resurtm) -- Bug #112: MSSQL: database abstraction layer now uses native transaction support of the SQLSRV driver (resurtm) -- Bug #124: Added CMysqlCommandBuilder to handle JOIN directive on update commands correctly (cebe, DaSourcerer) -- Bug #126: Fixed CWebUser::getReturnUrl(), allowing to determine default URL correctly, if CUrlManager::showScriptName is set to false (klimov-paul) -- Bug #138: CMysqlSchema fixed to support MySQL ANSI mode (cebe) -- Bug #140: Fixed validation CJuiButton with type buttonset (adminnu) -- Bug #162: Eventhandler attached twice when behavior is set enabled after attaching it (cebe) -- Bug #218: Fixed problem when using 'union' and 'order/limit/offset' in CDbCommand::buildQuery (nsanden) -- Bug #276: Tweaked CGridView stylesheet to include a hover style for the selected row (acorncom) -- Bug #810: Gii now adds a number to the end of relation name if same named relation already exists instead of not generating relation (n30kill, samdark) -- Bug #835: CApplication::onEndRequest is now called at the script shutdown to make sure log is written on forceful script termination (samdark, cebe) -- Bug #837: Fixed method CDbCriteria::__wakeup(), allowing to keep custom names for params and update all string parts for automatic params (klimov-paul) -- Bug #959: Bug where non-lowercase keys cannot be found in CConsoleApplication::$commandMap fixed (resurtm) -- Bug #962: Fixed handling of negative timestamps in CDateFormatter::format() (johnmendonca) -- Bug #1094: CListView with enabled history used to clear page title in case sorting or paging performed (Opera and Firefox only) (mdomba, resurtm) -- Bug #1095: Added missing retry_interval parameter of addServer function call in CMemCache (Lisio) -- Bug #1181: Fixed can read but not save binary data e.g. BYTEA on PostgreSQL (karmakaze) -- Bug #1212: Added missing .gitignore files to the application generated by WebAppCommand (resurtm) -- Bug #1249: CHttpRequest::sendFile() outputs malformed file content in some specific circumstances (andyhu) -- Bug #1279: CHttpRequest::sendFile() now always sends valid content-header (Ragazzo) -- Bug #1330: SQLite column default value was incorrect for column of type string and DEFAULT NULL (cebe) -- Bug #1344: Fixed URL problem in CGridView and CListView when enableHistory was true and unicode chars where used (mdomba, Redjik) -- Bug #1347: CDbTestCase: table name in fixtures list enclosed into double curly brackets (e.g. 'tasks'=>':{{task}}') didn't worked properly (resurtm) -- Bug #1351: CClientScript::registerMetaTag() now allows to register multiple meta tags with the same set of attributes (klimov-paul) -- Bug #1364: Empty CHtml::$errorCss cause class attribute rendering errors (creocoder) -- Bug #1381: Ignore select given by criteria or scope on stat relation (cebe) -- Bug #1392: HostInfo was appended to CHttpRequest::redirect() location in case of using protocol relative URLs (dInGd0nG, samdark) -- Bug #1406: Fixed the issue that false value returned from CDbCommand could not be cached properly (mdomba) -- Bug #1407: CDbCommandBuilder::createCountCommand() used to bind parameters for unused ORDER clause and unused SELECT part (resurtm) -- Bug #1444: Fixed CGoogleApi::register call to registerScriptFile (mdomba) -- Bug #1478: Fixed CWsdlGenerator error when using namespaced classes (Diego-Rocha) -- Bug #1485: CSort does not quote table alias when using CDbCriteria (undsoft) -- Bug #1492: Fixed jQuery calls when noConflict feature was used and jQuery code was returned by ajax calls (l-nagash) -- Bug #1499: Fixed CVarDumper highlighting "\" (antoncpu) -- Bug #1549: Fixed CFormatter::sizeFormat() to use translations from 'yii' category and corrected english default translation, also improved number formatting (cebe) -- Bug #1552: Fixed potential vulnerability in CJavaScript::encode(): $safe parameter didn't used to be passed to the recursive method calls (resurtm) -- Bug #1575: MessageCommand::extractMessages fails to ignore invalid category definitions (softark) -- Bug #1584: Fixed CGridView and CListView urls when enableHistory was used with "path" urlFormat (mdomba) -- Bug #1621: Fixed CCompareValidator to support {compareAttribute} in $message also on client-side (cebe) -- Bug #1622: CLocale::getTerritory() used to return invalid territory name when locales (language tags) were specified without territory part (e.g. 'de', 'fr') (resurtm) -- Bug #1624: Requirements page now tries all other preferred languages when the most preferred one is missing (ArtVal) -- Bug #1625: CEmailLogRoute does not properly encode UTF8 characters contained in logs, CEmailLogRoute::$utf8 property added (mdomba, resurtm) -- Bug #1628: Active HTTP sessions overwrote the Cache-Control header set by CHttpCacheFilter (DaSourcerer) -- Bug #1646: CLocale::getTerritory() used to return invalid territory name when locales (language tags) were specified with script part (e.g. 'zh-Hans-CN', 'zh-Hant-HK') (resurtm) -- Bug #1652: Fixed incorrect syntax of CDbSchema::renameTable() for SQLite, added CSqliteSchema::renameTable() method (Sarke) -- Bug #1673: CHttpRequest::getPreferredLanguage now works according to RFC2616. Added CHttpRequest::getPreferredLanguages that returns all user accepted languages in order of preference (Rupert-RR, bwoester, cebe, samdark) -- Bug #1676: Fixed listData() grouping when no group was specified (mdomba) -- Bug #1716: Fixed CCodeModel::pluralize() and CConsoleCommand::pluralize() so it doesn't force lowercase the first letter in any words (nsanden) -- Bug #1726: Fixed the error Undefined variable: json in CJSON.php when json_decode function did not exist (heyhoo) -- Bug #1792: Fixed persistent access permissions when two identities were used in a single application run. (jhenriquemc, François Gannaz) -- Bug #1853: CAssetManager::publish() method will throw an exception if $forceCopy and $linkAssets are both true (resurtm, mdomba) -- Bug: Table schema is refreshed on Gii model generation when schemaCachingDuration is used (SonkoDmitry) -- Bug: CDbCommand::setFetchMode wasn't accepting additional arguments needed for PDO::FETCH_CLASS (samdark) -- Bug: CCaptchaAction::validate check wasn't working properly in some cases (samdark, Qiang) -- Enh #84: Log route categories are now accepted in form of array. Added CLogRoute::except and parameter to CLogRoute::getLogs that allows you to exclude specific categories (paystey) -- Enh #104: Added CWebLogRoute::$collapsedInFireBug property to control whether the log should be collapsed by default in Firebug (marcovtwout) -- Enh #117: Added CPhpMessageSource::$extensionPaths to allow extensions, that do not have a base class to use as category prefix, to register message source (rcoelho, cebe) -- Enh #144: baseID in CHtml::checkBoxList and CHtml::radioButtonList can now be customized via htmlOptions (cebe) -- Enh #217: Added CTypeValidator::validateValue() and CTypeValidator::$strict, better check for arrays (samdark) -- Enh #259: CHttpRequest::getRestParams is now public (samdark) -- Enh #291: CFormatter::formatDate and formatDateTime now also accept strings in strtotime() format (francis_tm, cebe) -- Enh #486: CHttpSession::$gCProbability and CDbHttpSession::$gCProbability are floats now. Minimal possible $gCProbability value has been changed to the ≈0.00000005% (1/2147483647), was integer 1% before, default value left unchanged (1%) (resurtm) -- Enh #545: Add CDataProviderIterator to allow iteration over large data sets (phpnode) -- Enh #556: CDbColumnSchema::$comment property has been added. It stores comment for the table column, comment retrieving is working for MySQL, PgSQL and Oracle (resurtm) -- Enh #724: Third argument of CHtml::listData() now receives anonymous function as calculator of the text field value, PHP 5.3+ only (resurtm) -- Enh #846: Added addPrimaryKey() / dropPrimaryKey() commands to CDbMigration (ridget) -- Enh #949: Added COciSchema::resetSequence (jazahn, samdark) -- Enh #990: Added CArrayDataProvider::$caseSensitiveSort property which allows to control whether sorting should be case sensitive (resurtm) -- Enh #999: Changed md5() to crypt() in docs/guide/topics.auth, docs/blog and demos/blog. Added Wiki 425 tutorial on use of crypt() (tom--) -- Enh #1084: CDateTimeParser: MMM pattern for parsing short month names is now locale aware (resurtm) -- Enh #1146: CFileHelper::copyDirectory recursive directory creation (senz) -- Enh #1183: CDbCommand: added andWhere()/orWhere() which appends condition to the WHERE part of the query, in contrary of CDbCommand::where() which replaces it (resurtm) -- Enh #1184: CEmailValidator now supports IDN (Internationalized Domain Names), added CEmailValidator::$validateIDN property (resurtm) -- Enh #1134: CAssetManager filepath creation now can be extended thru generatePath(). Path creation is now recursive in publish(). (senz) -- Enh #1201: CMenu now supports HTML attributes for the links' wrap element specified in $linkLabelWrapper (resurtm) -- Enh #1228: Added ability to MessageCommand to generate translation files for multiple functions (Arne-S, samdark) -- Enh #1238: CJuiDatePicker is now using the native altField option to handle the hidden input when type flat is used (mdomba, christiansalazar) -- Enh #1282: CDateTimeParser: added MMMM pattern for parsing standard month names such as 'January', 'Décembre' or 'März' (resurtm) -- Enh #1286: CUrlValidator now supports IDN (Internationalized Domain Names), added CUrlValidator::$validateIDN property (resurtm) -- Enh #1289: Added support for column comments for MSSQL (CDbColumnSchema::$comment property) (resurtm) -- Enh #1299: Added CSRF token validation for PUT and DELETE (miraage, samdark) -- Enh #1369: Added CCheckBoxColumn::disabled that accepts PHP expression or anonymous function determining if checkbox for the row should be disabled (sucotronic) -- Enh #1386: Second parameter of the CHtml::value() is now able to accept anonymous function which calculates value to be used (Qiang, resurtm) -- Enh #1396: Added 'text/csv' mime-type for the 'csv' file extension in utils/mimeTypes.php (effectively used by e.g. CHttpRequest::sendFile()) (rawtaz) -- Enh #1426: Behaviors are now affecting memory consumption significantly less (slavcodev, creocoder, Qiang, samdark) -- Enh #1443: Added CHttpRequest::getRawBody() that allows reading RAW HTTP request body multiple times (itamar82, resurtm, samdark) -- Enh #1464: CCaptchaAction now supports rendering through ImageMagick, CCaptcha::checkRequirements() changed (resurtm, SonkoDmitry) -- Enh #1500: CSqlDataProvider now supports CDbCommand in constructor (slavcodev) -- Enh #1507: Added support for minOccurs, maxOccurs, nillable to CWsdlGenerator. Changed most of the methods visibility to protected. Added unit tests (lubosdz) -- Enh #1518: Allow to configure CHtml::$closeSingleTags and CHtml::$renderSpecialAttributesValue. Useful for HTML5 code (creocoder) -- Enh #1527: Added $id argument to CClientScript::registerMetaTag that allows you to override existing tags (Alex-Code) -- Enh #1531: CArrayDataProvider is now able to sort cutted array, where sorting column is not available in every entry (Yiivgeny) -- Enh #1535: HTML5 special attributes added to CHtml::renderAttributes() (creocoder) -- Enh #1538: Added CListView::ajaxUpdateError for the possibility to use a custom ajax error handler (mdomba) -- Enh #1567: Added filterSelector property to CGridView (MonkeyMaster) -- Enh #1581: Added 'unselectValue' to CHtml::dropDownList() and CHtml::activeDropDownList() to define default post value if no option is selected in multiple mode (creocoder, mdomba) -- Enh #1596: Added CGridView::rowHtmlOptionsExpression to allow set HTML attributes for the row (Ryadnov) -- Enh #1657: CDbCommandBuilder::createUpdateCounterCommand now can be used with float values (samdark, hyzhakus) -- Enh #1658: CFormatter::formatHtml() is now more flexible and customizable through new CFormatter::$htmlPurifierOptions property (resurtm) -- Enh #2343: Added CRequiredValidator::$trim property which determines to trim attribute value or not (AnatolyRugalev) -- Enh: Fixed the check for ajaxUpdate false value in jquery.yiilistview.js as that never happens (mdomba) -- Enh: Requirements checker: added check for Oracle database (pdo_oci extension) and MSSQL (pdo_dblib, pdo_sqlsrv and pdo_mssql extensions) (resurtm) -- Enh: Added CChainedLogFilter class to allow adding multiple filters to a logroute (cebe) -- Enh: Allow CDataProvider to use custom pagination and sorter (creocoder) -- Enh: Value of the CHtml::activeTextArea() can now be set through $htmlOptions['value'] (resurtm) -- Enh: Allow to customize CHtml::error() container tag (creocoder) -- Enh: CModule::setComponents() now can reconfigure already loaded components (creocoder) -- Enh: CLocale::getWeekDayName() weekday-number argument is now more compatible with date() function, allowing 0 and 7 for sunday (Yiivgeny, cebe) -- Enh: Added minified jQuery BBQ (samdark) -- Chg #1193: Upgraded jQuery to 1.8.3 (samdark) -- Chg #1373: Upgraded jquery star rating to 3.14 (samdark) -- Chg #1548: Upgraded jQueryUI to 1.9.2 (samdark) -- Chg #1563: Updated CTestCase for PHPUnit 3.7.7+ (tanakahisateru, samdark) -- Chg #1746: CDbCriteria::mergeWith() is now more consistent with all other class methods (hyzhakus, samdark) -- Chg: MSSQL unit tests updated and actualized, added SQLSRV driver support (resurtm) -- Chg: Added Oracle unit tests (resurtm) -- Chg: Updated CHttpCacheFilter to use dates as specified by RFC 1123 (bramp) -- Chg: Added punycode.js v1.1.1 (http://mths.be/punycode) and IDNA Converter v0.8.0 (http://phlymail.com/en/downloads/idna-convert.html) to the vendors (third party libraries and utilities) (resurtm) -- New: Ported offline documentation viewer from yiidoc project. Mainly usable for translations but can be used for reading offline as well (samdark) - -Version 1.1.12 August 19, 2012 ------------------------------- -- Bug #190: WSDL return tag was not generated by CWsdlGenerator when Macintosh line endings were used inside service describing docblock (resurtm) -- Bug #1066: CMemCache: expiration time higher than 60*60*24*30 (31536000) seconds led the value to expire right away after saving (resurtm) -- Bug #1072: Fixed the problem with getTableAlias() in defaultScope() (creocoder) -- Bug #1076: CJavaScript::encode() was not compatible with PHP 5.1 (samdark) -- Bug #1077: Fixed the problem with alias in CSort (creocoder) -- Bug #1083: CFileValidator is now unsafe by default. This will prevent setting attribute when no file was uploaded (samdark) -- Bug #1087: Reverted changes to CCookieCollection::add() introduced in 1.1.11 as they were triggering E_STRICT on some old PHP-versions (suralc) -- Bug #1088: Fixed usage of proper CActiveForm id property when it's supplied with htmlOptions (mdomba) -- Bug #1094: CGridView with enabled history used to clear page title in case sorting or paging performed (Opera and Firefox only) (resurtm) -- Bug #1109: Fixed "js:" encoding BC-break in CHtml::ajax() and related methods introduced in 1.1.11 (samdark) -- Bug #1120: Fixed duplicate events processing in CGridView when ENTER was pressed for filtering (mdomba) -- Bug #1192: CHttpCacheFilter failed to comply with RFC 2616, section 10.3.5 (DaSourcerer) -- Bug #1207: Fixed an issue in CHtml::resolveValue() which occurs when handling tabular data input (Qiang) -- Bug #1225: Fixed the bug that $.fn.yiiGridView.getChecked was not working always if a custom CGridView::template was used (mdomba) -- Bug #1243: Fixed the bug that when using CUrlManager::addRules with $append=false rules were added in reverse order (samdark) -- Enh #243: CWebService is now able to deal with the customized WSDL generator classes, was hardcoded to the CWsdlGenerator before, added CWebService::$generatorConfig property (resurtm) -- Enh #636: CManyManyRelation now parses foreign key for the junction table data internally, and provide public interface to access it (klimov-paul) -- Enh #1163: CGridview does not create empty class attributes anymore (cebe) -- Chg #1099: Changed connectionId dropdown to sticky text field in Gii model generator (mdomba) -- Chg #1167: Reverted back the change to CComponent::evaluateExpression() about global function support (Qiang) - -Version 1.1.11 July 29, 2012 ----------------------------- -- Bug #098: No correct identity value being returned when using Active Record and mssql (c-schmitz) -- Bug #114: CUniqueValidator and CExistValidator now respect table alias while creating db query condition (klimov-paul) -- Bug #145: CGettextMoFile now can parse strings with no context (eagleoneraptor) -- Bug #148: Fixed the bug in the blog demo that was not deleting right comment when not on the first page (mdomba) -- Bug #161: CCookieCollection::remove() now accepts an array of cookie options as a second argument to facilitate correct cookie removal (maximcherny) -- Bug #164: CEmailValidator.checkPort now checks the port 25 of listed MX servers (DaSourcerer) -- Bug #178: webapp creation with relative paths like ../ was not working correctly (cebe) -- Bug #193: Changed datetime column type for postgresql from 'time' to 'timestamp' (cebe) -- Bug #238: Fixed the problem that empty row could be selected in CGridView when there was no data (mdomba) -- Bug #295: Sometimes CJSON::decode returns null because native json_encode has bugs and returns null. Workaround to continue decoding when result of json_decode is null (luislobo) -- Bug #381: Fixed the bug that Gii model name input could get misspelled when autocomplete is used (mdomba) -- Bug #417: CAttributeCollections::mergeWith() does not take into account the caseSensitive (dmtrs) -- Bug #433: Fixed the bug that Gii model name input autocomplete was not working sometimes (mdomba) -- Bug #449: CDbHttpSession and CDbLogRoute now use query builder instead of DAO for proper quoting (mdomba, redguy) -- Bug #454: Removed translation on CDbConnection exception as it was creating an endless loop if the application used CDbCache (mdomba) -- Bug #517: Rule parameter sub-patterns are not checked correctly (ranvis) -- Bug #539: Fixed CUrlRule::createUrl() to treat sub-patterns as Unicode as parseUrl() does (ranvis) -- Bug #553: Criteria of related AR finders was affected after performing find with relational scopes (marcovtwout) -- Bug #618: Fixed caching of CWebUser::checkAccess() when it is called first time with and second time without $params (cebe) -- Bug #660: Fixed error when calling CDbCache::getValues (zilles) -- Bug #697: Fixed WSDLGenerator now generating proper namespace for certain complexTypes (BBoom) -- Bug #749: CActiveRecord::refresh() did not work in afterSave() for new records, will now always refresh, when db entry exists (cebe) -- Bug #769: Fixed the bug that $.fn.yiiGridView.getSelection was not working always if a custom CGridView::template was used (mdomba) -- Bug #772: CHttpRequest::getIsSecureConnection() was failing on some conditions (bulletbee, samdark) -- Bug #773: CGridView filters now filter on enter key in Internet Explorer (BBoom) -- Bug #803: Arbitary non-sorting links in CDataColumn's header were not working proper way (resurtm) -- Bug #827: Fixed the problem that CJuiSliderInput was rendering a name attribute for div element (mdomba) -- Bug #842: Active Records insert fails in MSSQL if a column has a default value of (NULL) (c-schmitz) -- Bug #852: Fixed the problem that CActiveForm was not revalidating fields if ajax submit was used (mdomba) -- Bug #859: Fixed CSort::applyOrder() and CSort::getOrderBy() to use custom table aliases (troch, samdark) -- Bug #865: CLogRoute called processLogs() even if log array was empty and caused empty emails and log files (cebe) -- Bug #879: Fixed a possible PHP error caused by CWebUser::restoreFromCookie() in combination with CHttpRequest.enableCookieValidation (kidol) -- Bug #901: Fixed possible encoding problem on exception (mdomba, samdark, cebe) -- Bug #1000: Added params to profiling token in CDbCommand::execute() to be consitent with CDbCommand::queryInternal() (cebe) -- Bug #1045: Building a query with empty array as parameter will not result in a broken sql-string anymore(suralc) -- Bug: Fixed CMenu::isItemActive() to work properly when there is a hash in the item's url (SlKelevro) -- Bug: Added missing return statement to CAuthItem->revoke() (mdomba) -- Bug: CHtml::resolveValue() ignoring of array elements accessor at the beginning of the $attribute argument now works properly (resurtm) -- Enh #120: Added ability to set cookies in an object based style without specifying the cookie-name twice (suralc) -- Enh #136: Added ability to select database connection in Gii model generator (samdark) -- Enh #157: Added ability to use models with objects implementing ArrayAccess as properties in CHtml::resolveValue (samdark) -- Enh #165: Allow CCacheDependency to be reusable across multiple cache calls (phpnode) -- Enh #171: Added support for PUT and DELETE request tunneled through POST via parameter named _method in POST body (musterknabe) -- Enh #179: CLogger now supports filtering profile timings by multiple & wildcard categories (intel352) -- Enh #191: Added ability to customize HTML classes of CLinkPager via its public properties (mashingan) -- Enh #206: Added ability to pass CDbCriteria object as AR relation parameter (samdark) -- Enh #215: Added tokens to CGridView::updateSelectors to allow adding custom selectors instead replacing only (mdomba) -- Enh #220: The URL pointing to the Google API in CGoogleApi is now protocol relative (suralc) -- Enh #237: The tabs of CTabView now support the property 'visible' (DaSourcerer) -- Enh #255: Sort CArrayDataProvider when elements is CActiveDataProvider or other object (rusmaxim) -- Enh #266: Add support for HTML5 url, email, number, range and date fields to CHtml (gregmolnar) -- Enh #267: CDbHttpSession is now able to store binary payload such as the output of the igbinary serializer (DaSourcerer, samdark) -- Enh #282: Added CCheckBoxColumn::headerTemplate to allow custom headers (mdomba) -- Enh #286: Added wildcard token to CDateTimeParser (cebe) -- Enh #294: Added deniedCallback to CAccessControlFilter and CAccessRule to allow forwarding control to a method on denial (luislobo) -- Enh #342: Added ability to pass parameters for RBAC bizRules from CAccessControlFilter configuration (claudejanz, samdark) -- Enh #356: Improved extendability of CDetailView by adding method renderItem() (cebe) -- Enh #369: Added $hashKey to CCache (kidol) -- Enh #414: Added sort parameter to yiic message command that sorts messages by key when merging (ranvis) -- Enh #455: Added support for default value in CConsoleCommand::prompt (eagleoneraptor) -- Enh #551: Added $safe parameter to CJavaScript::encode. If set to true, 'js:' will not be allowed. If you need to pass JavaScript, wrap your code with CJavaScriptExpression instead (samdark) -- Enh #552: Added support for http-level caching via CHttpCacheFilter (DaSourcerer) -- Enh #568: CHtml::getIdByName() will now convert spaces to underscore to get proper ID for HTML elements (mdomba) -- Enh #578: Added extension checks to CMemCache (samdark) -- Enh #581: Added formatSize method in CFormatter to format file sizes into units of different order - KB, MB, etc (brilyuhns, samdark) -- Enh #584: Refactored WebAppCommand to be more customizable, added more PHPDoc (samdark) -- Enh #599: Added case sensitivity check when autoloading classes (qiangxue) -- Enh #601: added the method loginRequired() to the IWebUser interface (mdomba) -- Enh #616: CVarDumper is now correctly highligting integer array keys (vernes, samdark) -- Enh #641: Added support for customizing serialization methods for cache components (DaSourcerer, Qiang) -- Enh #648: Added filterHtmlOptions property to the CGridColumn component (juban) -- Enh #652: Added namespace to yiiGridView events, so they can be easily removed by .off() jQuery method (Bethrezen) -- Enh #673: Changed CClientScript::scripts to be public (mdomba) -- Enh #675: CDateFormat::format() now returns null if the parameter $time is null (mdomba) -- Enh #690: Added sender name and proper headers for UTF8 encoding when sending e-mail in SiteController->actionContact() (mdomba) -- Enh #766: Added 'userId' to $params in CDbAuthManager::checkAccess() and CPhpAuthManager::checkAccess() (cebe) -- Enh #666: Added property $except to CValidator, a list of scenarios that the validator should not be applied to (resurtm) -- Enh #839: CListView::renderItems now resolves view file only once (nizsheanez) -- Enh #938: CFileValidator::sizeToBytes() is now public and available for using in the whole application (resurtm) -- Enh #943: CDateTimeParser is now able to parse short textual representation of month, e.g. Jan, Jun, Aug (resurtm) -- Enh #967: Commands from YII_CONSOLE_COMMANDS environment variable are now always added to yiic console application (schmunk) -- Enh: CFileValidator could validate uploaded file by its MIME-type, added $mimeTypes and $wrongMimeType properties (resurtm) -- Enh: Fixed romanian translation to use the better-supported cedilla characters (tudorilisoi) -- Enh: Added default value to CConsoleCommand::confirm (musterknabe) -- Enh: Allowed returning integer values as application exit code in CConsoleCommand actions (cebe) -- Enh: Added third parameter to CHttpCookie to configure the cookie by array (suralc) -- Enh: Added getIsFlashRequest(), proper handling of Flash/Flex request when using CWebLogRoute with FireBug (resurtm) -- Enh: Added CBreadcrumbs::$activeLinkTemplate and CBreadcrumbs::$inactiveLinkTemplate properties which allows to change each item's template (resurtm) -- Enh: Added full-featured behaviors and events CConsoleCommand::onBeforeAction & CConsoleCommand::onAfterAction (Yiivgeny) -- Enh: Added HTML5 history support on ajax requests on CGridView and CListView using History.js v1.7.2-r2 (https://github.com/balupton/history.js) from Benjamin Arthur Lupton (lightglitch) -- Enh: Changed CldrCommand to use medium dateTimeFormat and updated 18n data using newest(6546) CLDR (tanakahisateru) -- Enh: Added CErrorHandler::getHttpHeader() to send correct HTTP error codes (pgaultier) -- Enh: CGridView, only rows in tbody should have hover effect (mdomba) -- Enh: CClientScript::$defaultScriptFilePosition and CClientScript::$defaultScriptPosition for controlling default $position argument for registerScriptFile and registerScript (resurtm) -- Enh: CHttpCookie now implements __toString (suralc) -- Enh: Ability to set namespace for module controllers using CWebModule::controllerNamespace, documentation about using namespaced controllers and modules (samdark) -- Enh: Added possibility to set the container for CHtml::radioButtonList and CHtml::checkBoxList() (pgaultier) -- Enh: Added zii romanian(ro) translation; edited core messages to include proper romanian characters with diacritic marks (tudorilisoi) -- Enh: Added ILogFilter interface as an alternative to using CLogFilter as base class for implementing log filters (cebe) -- Enh: CAssetManager, added $forceCopy property which globally forces publication of asset files and directories (resurtm) -- Enh: WebAppCommand has ability to generate fresh application with git or hg specific files (resurtm) -- Enh: Gii default templates: added additional metadata (PHPDoc) of the variables passed into views for better IDE autocompletion (resurtm) -- Enh: WebAppCommand generated application: added additional metadata (PHPDoc) of the variables passed into views for better IDE autocompletion (resurtm) -- Enh #1053: CComponent::evaluateExpression will allow using global functions as callbacks (Ekstazi) -- Chg #384: CWebUser::changeIdentity() will now delete old unused session data file (Qiang) -- Chg #440: Upgraded JQuery UI to 1.8.22 (samdark) -- Chg #497: Added log component and preloaded it in default console application config in order to properly log errors (samdark) -- Chg: Upgraded jQuery to 1.7.2 (samdark) -- Chg: More unit tests added for CHtml (resurtm) -- Chg: Upgraded bundled markdown parser to v1.2.5 (DaSourcerer) -- New: Added TranslationsCommand build command aimed to help translation teams (samdark) - -Version 1.1.10 February 12, 2012 --------------------------------- -- Bug Fixed the bug introduced in 1.1.9 CActiveForm required field was not validated if left empty (mdomba) -- Bug #1799: Better fix for bug #1799 old fix was breaking CJuiDatePicker tabular input (mdomba) -- Bug #2284: Fixed the CActiveForm clientvalidation for tabular input (mdomba) -- Bug #3062: Fixed the bug that using yiilite.php and CLocale will cause exception (Qiang) -- Bug #3070: Fixed the CActiveForm JS error if there is no field rendered (mdomba) -- Bug #3071: Fixed the bug that afterValidateAttribute was not called properly (mdomba) -- Bug #3096: Fixed the bug when reporting an error and CHtml is not loaded (mdomba) -- Bug #3103: Fixed the bug that CActiveForm->error() was not overloading htmlOptions (mdomba) -- Bug #3107: Fixed the wrong encoding issue of Italian messages (Qiang) -- Bug #3108: Fixed the bug introduced in CActiveFinder::applyLazyCondition (Qiang) -- Bug #3166: Fixed the bug that CDbColumnSchema typecasted to NULL even for NOT NULL columns (Sam Dark) -- Enh #3063: Gii, when generating models, tableNames will be checked against reserved PHP keywords when '*' is used (mdomba) -- Enh #3097: Added CHttpRequest::decodePathInfo() (Y!!) -- Enh #3101: The methods of CSecurityManager do now work correctly for the case that mbstring.func_overload is in effect (Y!!) -- Enh #3112: Fixed the exception error display on ajax calls when YII_DEBUG is true (mdomba) -- Enh #3121: Added more rules for proper pluralization to the pluralize() method in CCodeModel and CConsoleCommand (mdomba) -- Enh #3153: CClientScript::addPackage() now returns CClientScript instance to support method chaining (Sam Dark) -- Enh #3154: Removed file existance check to allow relative path and added additional headers option to xSendFile() (mdomba) -- Enh #3169: Added CSort::SORT_ASC and CSort::SORT_DESC (Sam Dark) -- Enh: Added CActiveForm::validateTabular() to simplify ajax validation for tabular input (mdomba) -- Chg: HTML-encoded input values for exist and unique validators (Qiang) -- Chg: Upgraded JQuery UI to 1.8.17 (mdomba) -- Chg: Upgraded HTMLPurifier to v4.4.0 (Sam Dark) - - -Version 1.1.9 January 1, 2012 ------------------------------ -- Bug: Removed unnecessary COciCommandBuilder::createInsertCommand quotes (Sam Dark) -- Bug: CHttpRequest.sendFile() gives incorrect content length when output_handler is enabled through code or non output_handler directive (Sam Dark) -- Bug #1356: Fixed CActiveForm ajax validation when checkBoxList or radioButtonList are used (mdomba) -- Bug #1968: Fixed inconsistence in CActiveForm error highlighting when checkBoxList or radioButtonList are used (mdomba) -- Bug #2603: Fixed the bug that CDbHttpSession::regenerateID call when session isn't started results in SQL error (Sam Dark) -- Bug #2623: Fixed the bug that by setting multiple classes in CGridView itemsCssClass prevents rows being selected (mdomba) -- Bug #2635: MigrateCommand migration execution time is now measured correctly (Sam Dark) -- Bug #2636: CConsoleCommand::init() wasn't called in yiic shell mode (Sam Dark) -- Bug #2773: Fixed possible CUrlManager::createUrl parameters conflict when using custom URL rule classes (Sam Dark) -- Bug #2581: Fixed CJuiTabs - not replacing id slug in header tool tips (sebas) -- Bug #2643: Output buffer wasn't properly cleaned on displaying error screen (Sam Dark) -- Bug #2733: Fixed CDbCriteria parameter names collision on unserialize (mcheale, Sam Dark) -- Bug #2786: Fixed inheritance in CLDR months parsing (mcheale, Sam Dark) -- Bug #2822: Fixed warning when "Host:" isn't present or is empty in HTTP request headers (Sam Dark) -- Bug #2853: Fixed sending of the button name in CActiveForm with enableAjaxValidation enabled (mdomba) -- Bug #2861: Removed the nested container css class in the skeleton application views (Qiang) -- Bug #2915: Fixed client validation in CCompareValidator to compare numbers instead of strings (mdomba) -- Bug #2932: CAuthItem::getType() returns string while using CDbAuthManager (Sam Dark) -- Bug #2999: CSort::getDirections, error when array is passed via $_GET (Sam Dark) -- Bug #3018: Fixed CACtiveForm ajax validation when checkBox or radioButton are used (mdomba) -- Bug #3029: Fixed the bug that empty items were not hidden when CMenu::hideEmptyItems is true (mdomba) -- Bug #3033: Fixed proper array merging in CDirectoryCacheDependency->generateTimestamps (mdomba) -- Bug #3041: Fixed possible infinite loop while processing logs (Yiivgeny, Sam Dark) -- Bug #3042: Fixed the bug that CHttpSession::setCookieMode wasn't setting session.use_only_cookies when 'none' value was used (Sam Dark) -- Chg: Upgraded JQuery UI to 1.8.16 (Sam Dark) -- Chg: Upgraded jQuery to 1.7.1 (Sam Dark,mdomba) -- Chg: Upgraded CMaskedTextField jQuery plugin (Masked Input) to 1.3, added minified version (Sam Dark) -- Chg: Reverted back the changes made to fix issue 2284 (Qiang) -- Chg #2647: Fixed inconvenient way of defining through relation (creocoder, Sam Dark) -- Chg #2951: Removed CConfiguration::createObject, CController::paginate and CHtml::getActiveId deprecated since 1.0.x (Sam Dark) -- Chg #3054: CComponent::__isset properly checks for null values (mdomba) -- Enh #2029: Added scope support to Model::relations() (creocoder, Sam Dark) -- Enh #2129: Added Monospace font as a fallback for source code on the exception view page (mdomba) -- Enh #2231: Added CMenu::itemCssClass for the possibility to assign one CSS class to all menu items (mdomba) -- Enh #2334: CHttpRequest::getPathInfo() now properly decodes both UTF-8 and ISO-8859-1 encoded URIs (Sam Dark) -- Enh #2387: Numeric keys are now displayed in error/exception stacktrace call argument if array isn't 0..X indexed (Sam Dark) -- Enh #2602: Better error handling in CHttpSession::open() when using PHP <5.3.0 (Sam Dark) -- Enh #2604: CArrayDataProvider::keyField can now be set to false to use keys from $rawData array instead of a named keyField (creocoder, Sam Dark) -- Enh #2637: Related table alias set dynamically in relational query is now available in the scopes of related model (creocoder, Sam Dark) -- Enh #2646, #2706: Added ability to join on a specific keys (creocoder, Sam Dark) -- Enh #2654: Enhanced CUrlManager::addRules() by allowing new rules to be inserted in front of the existing rules (Qiang) -- Enh #2715: CMap::mergeArray now can accept multiple arrays to be merged (firejdl, Sam Dark) -- Enh #2717: Extracted MigrateCommand::createMigrationHistoryTable method from MigrateCommand::getMigrationHistory (Sam Dark) -- Enh #2751: Added removeOld parameter to yiic message command that allows not to add obsolete lines to translation file generated (luislobo, Sam Dark) -- Enh #2795: Added Yii::t() to YiiBase::powered() (Sam Dark) -- Enh #2808: Added ability to override core classes using YiiBase::classMap (Sam Dark) -- Enh #2811: Fully automated CLDR data update, updated data to 2.0.1. Added getLanguageID, getScriptID, getTerritoryID, getRegionID, getLocaleDisplayName, getLanguage, getScript, getTerritory methods to CLocale (kshaw, Sam Dark) -- Enh #2823: Added autocomplete for Gii "Table Name" field (Sam Dark) -- Enh #2855: CWebUser::login() returns the login status (mdomba) -- Enh #2872: Added CConsoleCommand::prompt() that asks for input and CConsoleCommand::confirm() that asks for confirmation (Sam Dark) -- Enh #2890: Added CInlineValidator::clientValidate to set custom client validation (mdomba) -- Enh #2914: Added CClientScript::addPackage (Sam Dark) -- Enh #2929: Added forceDownload to xSendFile options to choose between attachment and inline disposition. (mdomba) -- Enh #2981: Added CHtml::liveEvents to set the default global style for attaching jQuery event handlers. (mdomba, Sam Dark, Ekstazi) -- Enh #3020: Added HTTP_REFERER information to the exception log (mdomba) -- Enh #3024: Added CDbMigration::refreshTableSchema() that refreshes specified table schema cache (Sam Dark) -- Enh: Documented component accessors with @property for significantly better IDE autocomplete (Sam Dark, Detonator, Athari) -- Enh: Added CWebUser->loginRequiredAjaxResponse - value to be returned for ajax calls in case the user session has expired (mdomba) -- Enh: CFileCache::get() does now suppress a possible PHP error which might occur on concurrent requests (Y!!) -- Enh: jquery.yiiactiveform.js added check for form visibility to validate() to prevent JS error when using CActiveForm with jQuery dialog (mdomba) -- Enh: removed the check for ajax call in CErrorHandler::handleException() (mdomba) -- Enh: CAssetManager now generates different hash for files/directories with different mtime (Sam Dark) -- Enh: Yii error screen will now display proper message like error/warning/notice (mdomba) -- Enh: CHtml::clientchange() now uses the new jQuery on() method for event binding (mdomba) - -Version 1.1.8 June 26, 2011 ---------------------------- -- Bug: Fixed a typo that may cause issue when setting custom script packages with baseUrl option for CClientScript (Qiang) -- Bug #2001: CGridView now renders the body after the footer in order to conform to the standard (Qiang) -- Bug #2236: CJuiTabs - added id to ajax tabs (sebas) -- Bug #2272: Fixed the bug of undefined index css in CTreeView (mdomba) -- Bug #2274: CDbCriteria can't merge "with" anymore if a scope applied another "with" condition (Sam Dark, Michael) -- Bug #2275: Fixed the bug that ajax error handler was not called in case of exception (mdomba) -- Bug #2284: Fixed the bug that CActiveForm clientValidation did not work with tabular input forms (Qiang) -- Bug #2292: Fixed the bug that CActiveDataProvider may ignore the specified criteria for the sorting configuration (Qiang) -- Bug #2294: Fixed the bug that duplicated PK columns may appear in the join SQL statement if custom select option is used (Qiang) -- Bug #2303: Fixed not logging anything on WSDL service failure (Sam Dark) -- Bug #2312: Fixed the bug that auto-incremental columns for MSSQL may return non-integers as last insert ID (Qiang) -- Bug #2328: Fixed the bug that table names was not quoted in CDbAuthManager (mdomba) -- Bug #2338: Fixed a typo in the client validation code for CNumberValidator (Qiang) -- Bug #2359: Fixed the bug that checkbox in Gii view template may be hidden automatically (Qiang) -- Bug #2377: Fixed the bug that jsonp was expected instead of JSON when using AJAX and CAutoComplete at the same page (Sam Dark) -- Bug #2382: Fixed the bug that yiic wasn't able to run if there is no commands dir in application (Sam Dark) -- Bug #2394: Fixed a typo in CDbCache that may cause mget() to fail (Qiang) -- Bug #2409: Fixed the bug that CCaptcha::buttonOptions is not respected (Qiang) -- Bug #2411: Fixed the bug that CCaptcha will fail when setting buttonType to be 'button' (Qiang) -- Bug #2422: Fixed the bug that calling CLogger::flush(true) multiple times may cause duplication of dumped messages (Qiang) -- Bug #2426: Fixed the bug in Gii about calling a non-static method in a static way (Qiang) -- Bug #2463: Fixed the bug that INSERT statement created by COciCommandBuilder may fail in some cases (Qiang) -- Bug #2475: Fixed the bug that CMssqlCommandBuilder and COciCommandBuilder don't respect parameters declared in CDbExpression when doing insertion and updating (Qiang) -- Bug #2485: Fixed the bug that CMssqlPdoAdapter is not used when the driver is sqlsrv (Qiang) -- Bug #2509: Fixed the bug that AR for MSSQL may fail if on different catalogues (Qiang) -- Bug #2516: CTimestamp::getDate() produced wrong output with the default timestamp (Y!!) -- Bug #2538: Fixed the bug that AR may join with incorrect columns (Qiang) -- Bug #2544: Fixed the bug that setting CJuiDatePicker.language to be 'en' will use wrong language (Qiang) -- Bug #2574: Fixed the bug that overriding CActiveRecord::primaryKey() does not set the isPrimaryKey flag for columns (Qiang) -- Bug: CMapIterator current key wasn't initialized properly (Sam Dark, Detonator) -- Bug: Controller generated with Gii CRUD wasn't able to handle non-integer primary key (Sam Dark) -- Bug: Query caching may give incorrect caching results when bindParam or bindValue is used (Qiang) -- Bug: Changing CActiveForm.errorMessageCssClass had no effect when ajax and client validation were disabled (Y!!) -- Bug: Error when using CUniqueValidator with models indexed by specific field (Sam Dark, Yiivgeny) -- Bug: Fixed the bug that CAssetManager doesn't set permission mode according to newDirMode and newFileModel properties when publishing a directory (Qiang) -- Enh #2319: Added support to call behavior scope through criteria 'with'=>array('scopes'=>'behaviorScope') (Sam Dark, creocoder) -- Enh #2262: Added warning log when a session fails to start by CHttpSession (Qiang) -- Enh #2264: Added an option to the model code generator such that the relation generation can be disabled (Qiang) -- Enh #2268: Added CClientScript::getPackageBaseUrl() (Qiang) -- Enh #2273: Used better merging algorithm to build query parameters that are of array type in CUrlManager (Qiang) -- Enh #2299: Added CAssetManager.newFileMode and newDirMode (Qiang) -- Enh #2325: Added $option parameter to CDbCommand::select() to support special SELECT syntax (Qiang) -- Enh #2341: More verbose log message for CModel::onUnsafeAttribute. Added model class (Sam Dark) -- Enh #2357: Documented CWebApplication accessors with @property for better IDE autocomplete (Sam Dark) -- Enh #2361: Added CDbConnection::pdoClass that allows to specify and use custom PDO wrapper class (Sam Dark) -- Enh #2365: Added support for creating more complex index by using createIndex() of query builder. (Qiang) -- Enh #2386: Added CController::renderClip() (Qinag) -- Enh #2389: MessageCommand now accepts overwrite option determining if merge result will overwrite existing file (Sam Dark) -- Enh #2410: Improved CHtml::error() so that it can take attribute names in tabular format (Qiang) -- Enh #2424: CDbConnection::beginTransaction() will now trigger a trace message for better debugging (Y!!) -- Enh #2436: Added support for allowing console applications to call createUrl() (Qiang) -- Enh #2450: Added Ctype extension check to Yii requirements checker (Sam Dark) -- Enh #2474: Enhanced CDbCommand::insert() and update() to support CDbExpression (Qiang) -- Enh #2483: Added CGridView::$ajaxUrl and CListView::$ajaxUrl (Qiang) -- Enh #2493: Added money column type to the query builder (Qiang) -- Enh #2500: Added possibility to use a custom click handler for CButtonColumn default delete button (mdomba) -- Enh #2524: CActiveRecord::exists() now respects the scopes applied (Qiang) -- Enh #2532: Improved Yii class autoloader to support Web servers that do not allow changing PHP include paths. (Qiang) -- Enh #2534: Added CHtml::decode() (Qiang) -- Enh #2535: Added YiiBase::setLogger() (Qiang) -- Enh #2555: Exposed CFileCache::gc() so that garbage collection can be explicitly invoked (Qiang) -- Enh #2556: Improved exception display in ajax mode (Qiang) -- Enh #2571: Improved the code for cleaning output buffers in CErrorHandler (Qiang) -- Enh: XHR is now passed to CButtonColumn error JavaScript callback as a first argument (Sam Dark) -- Enh: Added CHttpSession::regenerateID() and improved CWebUser::changeIdentity() by regenerating session ID (Qiang) -- Enh: Added CActiveRecord::saveCounters() (Qiang) -- Enh: Added Brazilian Portuguese translation (pt_br) of the core messages (bastardgoblin) -- Enh: CJSON::encode() can now encode non-UTF8 data (Qiang) -- Enh: Added CLogger::autoDump to allow writing log messages to destinations in "real time" (Qiang) -- Enh: Added support for using custom URL rule classes with CUrlManager (Qiang) -- Enh: Added input length check to email and url validators to improve security (Qiang) -- Enh: Added support to allow registering a class autoloader after Yii's default autoloader (Qiang) -- Enh: Unit tests for validators (Kevin Bradwick) -- Chg #2251: Changed the constructor of CUploadedFile to be public (Qiang) -- Chg #2258: Added support to invalidate cached content by setting COutputCache::duration to be 0 (Qiang) -- Chg #2261: Upgraded HTMLPurifier to v4.3.0 (Sam Dark) -- Chg #2309: Changed XML mimetype to application/xml for more interoperability (Sam Dark) -- Chg #2370: Upgraded JQuery UI to 1.8.13 (Sam Dark) -- Chg #2401: Upgraded jQuery to 1.6.1 (Sam Dark) -- Chg #2452: Upgraded Blueprint CSS to 1.0.1 (Sam Dark) -- Chg #2482: CWebService will not display source file name and error line number in production mode (Qiang) -- Chg #2496: Setting CDbConnection::$emulatePrepare to be false will now explicitly set PDO::ATTR_EMULATE_PREPARES to be false (Qiang) -- Chg: Changed CHtml::clientChange event binding to support custom event types and avoid conflicts when using AJAX (Sam Dark) -- Chg: Changed all js live() calls with on() as live() is deprecated (mdomba) - -Version 1.1.7 March 27, 2011 ----------------------------- -- Bug #1080: Correct recursive merging for CDbCriteria::with (creocoder, Sam Dark) -- Bug #1624: Fixed the bug that Gii would generate only one relation for a parent that has a child with more FK linking to it (mdomba) -- Bug #1809: Fixed the bug that CPgsqlSchema did not detect sequence names correctly in some scenarios (Qiang) -- Bug #1984: Fixed firing event multiple times when using live()/delegate() on AJAXified pages (Ekstazi, Sam Dark) -- Bug #2026: Fixed the bug that migration command does not respect the connectionID property value (Qiang) -- Bug #2032: Fixed the bug that beginCache with renderDynamic was not working if used multiple times (mdomba) -- Bug #2037: Fixed CGridView js bug on selectionChanged "sboxname is undefined" (mdomba) -- Bug #2060: Fixed the bug that CWebUser::getFlashes() would return a counter array in the result (Qiang) -- Bug #2097: CHttpRequest::getUrl() should be the same as getRequestUri (Qiang) -- Bug #2099: Fixed CDbCriteria::mergeWith error (creocoder, Sam Dark) -- Bug #2107: Fixed the bug that calling CSqliteSchema::resetSequence() may throw exception when no autoincrement column (Qiang) -- Bug #2130: Fixed bug that Gii code/diff view was not starting from top (mdomba) -- Bug #2131: Fixed the bug that CGridView ajax calls would sometime display error alert when leaving the current page (mdomba,Qiang) -- Bug #2136: CGridView filter now uses jQuery serialize() instead of param() so that a checkbox can be used as a filter (mdomba) -- Bug #2140: Fixed the problem that CGridView even rows where not properly rendered in Firefox and Chrome (mdomba) -- Bug #2146: Fixed the bug in CFileHelper::getExtension, validatePath and getMimeTypeByExtension that was not finding the file extension correctly (mdomba) -- Bug #2169: Fixed the bug that some columns are not properly quoted in MigrateCommand (Qiang) -- Bug #2178: Fixed the bug that query builder did not recognize the AS keyword when using table alias (Qiang) -- Bug #2183: Fixed the bug that calling CActiveDataProvider::getTotalItemCount() explicitly would make the applied scopes disappear (Qiang) -- Bug #2188: 'join' in default scopes is now respected by STAT relations (creocoder, Sam Dark) -- Bug #2202: Fixed the bug that when setting CJuiDatePicker.flag=true, the date picker would not appear (Qiang) -- Bug #2214: Fixed the bug that renameColumn for MSSQL did not work correctly (Qiang) -- Bug: Fixed the bug that a PHP notice may occur in exception view if a method in the call stack has a very complex signature (Qiang) -- Bug: Fixed error that CGridview breaks when updating non-cgridview elements (mdomba) -- Bug: Fixed the bug in CCheckBoxColumn, "check all" checkbox was not being checked/unchecked when needed (mdomba) -- Bug: Fixed the bug in CGridView, selectionChanged was not called when "check all" was clicked (mdomba) -- Bug: Fixed resetting sequence in CDbCommand::truncateTable (Sam Dark) -- Bug: Fixed CMemCache incompatibility with some pecl-memcache and memcached versions (Sam Dark) -- Enh #558, #1755: Added parametrized named scopes, added scopes to criteria, implemented scope criteria merging (creocoder, Sam Dark) -- Enh #802: Added RESTful URL management (Qiang) -- Enh #923: Improved CUrlManager::parsePathInfo() to support multi-dimensional input arrays (Qiang) -- Enh #1117: Added support for "through" in Active Record relations allowing to handle association table data (creocoder, Sam Dark) -- Enh #1285: Added support for using custom script packages with CClientScript (Qiang) -- Enh #1741: Exposed CActiveForm::attributes and summaryID (Qiang) -- Enh #1770: Added CDbColumnSchema::autoIncrement property to allow checking whether a DB column is auto-incremental (Qiang) -- Enh #1782: Added updateSelector property to both CGridView and CListView (Qiang) -- Enh #1786: Enhanced CUrlValidator by adding 'validSchemes' and 'defaultScheme' property (Y!!) -- Enh #1784: Enhanced CWidget::getViewPath() to support returning themed view path (Qiang) -- Enh #1792: Enhanced CGridView: on ajax error a proper message is composed and displayed or optionally sent to the custom error handler (mdomba) -- Enh #1795: Added CFormInputElement::$enableAjaxValidation and $enableClientValidation to allow turning on/off AJAX validation for individual input fields (Qiang) -- Enh #1816: Added $dumpLogs parameter to CLogger::flush() so that log messages can be forced to be dumped at will (Qiang) -- Enh #1843: Added 'uncheckValue' option to CHtml::activeRadioButtonList and CHtml::activeCheckBoxList. It allows to avoid hidden field rendering (creocoder, Sam Dark) -- Enh #1847: Exposed CClientScript::$hasScripts (Qiang) -- Enh #1852: Added CWebUser::authTimeout to support separation between authentication timeout and session timeout (Qiang) -- Enh #1868: CDbConnection will now open a DB connection only when needed, unless autoConnection is set true (Qiang) -- Enh #1937: Added support to use custom input ID for input fields that need AJAX-based validation (Qiang) -- Enh #1993: Allow AR relations across separate db connections (Qiang) -- Enh #1996: Added support for using parameter binding with class-based actions (Qiang) -- Enh #1999: Added CCaptchaAction::offset property in order to decrease or increase the readability of the captcha (Y!!) -- Enh #2011: Added CDbCommand::setFetchMode to allow setting PDO result fetching mode (Sam Dark) -- Enh #2013: When creating model with Gii, database field names will be checked to conform with PHP variable naming rules (mdomba) -- Enh #2024: Added CHttpRequest::getPut() and getDelete() to fully support RESTful requests (Qiang) -- Enh #2059: Added support to respect the "target" attribute of an element generated by CHtml with "submit" HTML options (Qiang) -- Enh #2063: The CActiveForm JavaScript should now correctly trigger validaton for checkbox and radio type input fields (Y!!) -- Enh #2068: CTimestampBehavior::timestampExpression can now be a DB expression (Qiang) -- Enh #2093: CDataColumn will now always render a filter if the filter property is a string (Qiang) -- Enh #2094: Added SQL statement display in debug mode when an error occurs while executing a SQL (Qiang) -- Enh #2105: Added CButtonColumn::afterDelete so that a custom javascript function can be called after the delete function (mdomba) -- Enh #2108: Added CGridView::blankDisplay to allow customizing blank cell display (Qiang) -- Enh #2125: Added memcached check and hint to requirements checker (Sam Dark) -- Enh #2133: Set default focus to the password input for Gii login page (Qiang) -- Enh #2141: Allow the 'label' option of CMenu menu items to be optional and take an empty string value (Qiang) -- Enh #2142: Added CWebUser::autoUpdateFlash (Qiang) -- Enh #2143: Added htmlOptions to CTreeView::data so that additional options can be set for any tree view node (mdomba) -- Enh #2172: Added CDbMigration::execute() (Qiang) -- Enh #2179: Added CMultiFileUpload::options so that additional options can be passed to the constructor of the multifile object (mdomba) -- Enh #2185: Allow the column type to be optional when specifying columns for CGridView (Qiang) -- Enh #2197: Added $escape parameter to CDbCriteria::compare() (Qiang) -- Enh #2198: Improved CJuiTabs so that the tab content can be skipped (Qiang) -- Enh #2199: Added CListView::separator (Qiang) -- Enh #2206: Added $clearErrors parameter to CModel::validate() (Qiang) -- Enh #2209: Added CDbConnection::setAttributes() and getAttributes() to support initializing DB connection with PDO attributes (Qiang) -- Enh #2226: Added more tokens to summaryText when CBaseListView.enablePagination is set false (Qiang) -- Enh #2227: Exposed CActiveRecord::query() (Qiang) -- Enh: Added CGridView::ajaxUpdateError for the possibility to use a custom ajax error handler (mdomba) -- Enh: Allowed using CController instead of Controller with webapp generated application (Sam Dark) -- Enh: Added ability to perform Relational query without getting related models (creocoder, Sam Dark) -- Enh: Error page now displays associative array keys in parameter list (Sam Dark) -- Enh: Added CController::getActionParams() and invalidActionParams() to allow customizing action parameter binding feature (Qiang) -- Enh: Added CEvent::$params (Qiang) -- Enh: CStringValidator now uses application charset by default if mb_strlen is available (Sam Dark) -- Chg #2001: CGridView now renders footer after the body content (Qiang) -- Chg #2111: Calling CActiveRecord::getRelated($name, true) now will redo the DB query even if isNewRecord is true (qiang) -- Chg #2144: Upgraded jQuery UI to version 1.8.11 (Sam Dark) -- Chg #2148: Upgraded jQuery to version 1.5.1 (Sam Dark) -- Chg #2163: CConsoleCommand::usageError() will now exit with error code 1 (Qiang) -- Chg: jQuery UI now uses minified CSS (Sam Dark) -- Chg: Removed jQuery dimensions plugin since it's in jQuery core (Sam Dark) -- Chg: Upgraded bgiframe to 2.1.2 (Sam Dark) -- New #1763: Added support for performing seamless client-side data validation using CActiveForm (Qiang, hightman) -- New #2069: Added CDateValidator (Qiang) -- New: Added support for query caching (Qiang) -- New: Added Lithuanian translations (tomas.valacka) - -Version 1.1.6 January 16, 2011 ------------------------------- -- Bug #997: Fixed the bug that relational AR query with page-by and sorting may fail to work for SQL Server (Qiang) -- Bug #1775: Fixed the bug that AR and Gii may fail for tables not in default schema in Oracle DB (Qiang) -- Bug #1790: Fixed the bug that CJSON::encode may generate invalid encoding result when data contains float numbers (Qiang) -- Bug #1799: Fixed the bug that CJuiDatePicker::$name may cause a PHP error (Y!!) -- Bug #1819: CHttpRequest::getPathInfo() now respects encoded characters (Sam Dark, creocoder) -- Bug #1851: CFileHelper::getMimeType() was causing an error if used with PHP 5.2 and PECL fileinfo extension (Sam Dark) -- Bug #1858: Fixed the bug that CDbCommandBuilder::createInCondition() doesn't work with composite keys (Qiang) -- Bug #1864: Fixed a typo in CDbCommandBuilder that disables correct handling of group and having in createCountCommand (Qiang) -- Bug #1878: Fixed the issue that keys rendered in grid view and list view should be encoded (Qiang) -- Bug #1879: Fixed the issue the AR does not work with PostgreSQL array column type (Qiang) -- Bug #1891: Fixed the bug that on CListView ajax request was generating a DOM container inside itself (mdomba) -- Bug #1902: Fixed the issue that CActiveRecord::exists() may cause ambiguous column error when used in relational query (Qiang) -- Bug #1920: Fixed the issue that the summary displayed by CGridView and CListView may be incorrect for SQL Server (Qiang) -- Bug #1936: Fixed the issue that flat CJuiDatePicker is not closing it's tag correctly (sebas) -- Bug #1942: Fixed the bug that CSecurityManager::computeHMAC() generates non-standard HMAC (Qiang) -- Bug #1945: Fixed the bug that user-supplied form ID is not honored when building a form using CForm with a model (Qiang) -- Bug #1948: Fixed a bug in generating the number symbols of I18N data from CLDR (Qiang) -- Bug #1975: Fixed the bug that caused a PHP error when CAssetManager::publish() tried to create a symlink in a non-existing directory (Y!!) -- Bug: Fixed the bug that CActiveForm generates unnecessary js code about setting focus (Qiang) -- Bug: Fixed CDateTimeParser::parse() default hour, minute and second handling when they are not used in pattern (Sam Dark) -- Enh #1733: Updated multifile plugin used by CMultiFileUpload to version 1.47 (mdomba) -- Enh #1771: Added $driverOptions parameter to CDbCommand::bindParam() method (Qiang) -- Enh #1785: Added CAssetManager::$excludeFiles property to support exclusion of irrelevant files from the publishing process (Y!!) -- Enh #1836: The contact form model of the blog demo does now make use of CCaptcha::checkRequirements (Y!!) -- Enh #1842: CHtml::button will not render the name attribute if it is set null (Qiang) -- Enh #1860: Changed the signature of CValidator::createValidator() to make it easier to use (Qiang) -- Enh #1849: Updated Blueprint CSS to version 1.0 (sebas) -- Enh #1872: Added $defaultUrl parameter to CWebUser::getReturnUrl() (Qiang) -- Eng #1875, #1987: Added support for CLDR-based plural forms format and number placeholders to Yii::t (creocoder, Sam Dark, Qiang, dmitriy.trt) -- Enh #1877: createAbsoluteUrl in CWebApplication and CController will now respect URL rules that already have host info built-in (Qiang) -- Enh #1885: Added ipFilters to the Gii-created config file to reduce user confusion (Sam Dark, Steve Friedl) -- Enh #1895: Added CDbDataReader implements Countable interface (mdomba) -- Enh #1899: Added checkIntegrity and resetSequence for SQL Server (Qiang) -- Enh #1929: Improved CActiveForm so that it can be used multiple times on the same page for the same type of data model (Qiang) -- Enh #1931: CDbConnection.tablePrefix can now use an empty string as table prefix (Qiang) -- Enh #1962: Added submenuOptions option to CMenu::items (Qiang) -- Enh #1995: Added CDbConnection::driverMap to allow more easily customizing schema classes (Qiang) -- Enh: Updated CLDR data to version 1.9 (Sam Dark) -- Enh: Allowed passing multiple forms or choice format quantity parameter without wrapping it with array (Sam Dark) -- Enh: CDbConnection::quoteColumnName and quoteTableName will properly quote table prefix and schema prefix. (Qiang) -- Enh: Added CConsoleCommand::init() (Qiang) -- Enh: Improved the exception display with source code for each call stack (Sam Dark, Qiang) -- Enh: Improved the error display in console command mode (Qiang) -- Enh: Added support for using anonymous parameters and global options in console commands (Qiang) -- Enh: Added message translations in Czech and Croatia (Qiang) -- Enh: Enhanced CFileLogRoute to process the logs faster (Y!!) -- Enh: Improved IDE code completion for Yii::app()-> (Sam Dark) -- Enh: CSort now supports relation.field notation to sort grids by related model fields (Sam Dark, denis909) -- Enh: Added CHttpRequest->xSendFile() to process file download requests by using X-Sendfile header (mdomba) -- Enh: Refactored CMenu by adding CMenu::renderMenuItem to make it easier to be extended (Qiang) -- Enh: Refactored CCheckBoxColumn for better use, added CCheckColumn->selectableRows (mdomba) -- Chg #1914: Composite foreign keys should be separated by commas in CActiveRecord::relations() (Qiang) -- Chg #1949: CGridView will now display the first page after changing filters (Qiang) -- Chg: isset($model->x) and isset($model['x']) are now identical for CActiveRecord models (Sam Dark) -- Chg: Changed CHtml::clientChange() to make $live a configurable option in $htmlOptions (Qiang) -- New #1191: Implemented the database migration feature (Qiang) -- New: Added query builder (Qiang) - - -Version 1.1.5 November 14, 2010 -------------------------------- -- Bug #997: Fixed the bug that relational AR query with page-by and sorting may fail to work for SQL Server (Qiang) -- Bug #1130: Fixed the bug when renderDynamic and beginCache was used together without page caching (mdomba) -- Bug #1244: Fixed the bug that CDbCommandBuilder::createCountCommand may generate invalid SQL when having and/or group options are used (Qiang) -- Bug #1420: Fixed the bug that the table alias set in the model was not honored in STAT AR queries (Qiang) -- Bug #1565: Fixed the bug that COutputCache may fail to work when used to cache whole pages (Qiang) -- Bug #1577: Fixed the bug in CMssqlSchema::compareTableNames() (Qiang) -- Bug #1592: Fixed the bug that the hidden field generated by CHtml::checkBox may have the same ID as the checkbox. (Qiang) -- Bug #1615: Fixed the bug that caused CLogFilter::filter() to add context informations when the log was empty (Y!!) -- Bug #1643: Fixed the bug that CFileValidator may cause a PHP error when using maxFiles>1 and the model attribute returning unexpected array (Qiang) -- Bug #1647: Fixed the bug that CActiveRelation may attempt to set an undefined 'together' property when merging with a criteria (Qiang) -- Bug #1653: Fixed the bug that in PHP 5.3 CArrayDataProvider will fail due to incorrect parameters sent to array_multisort (Qiang) -- Bug #1655: Fixed the bug in COciSchema about checking DB schema (Qiang) -- Bug #1673: Fixed the bug that CDbSchema::getTables() might return null table schemas (Qiang) -- Bug #1685: Fixed the bug in COciSchema that will fail when used with DB schema (Qiang) -- Bug #1696: Fixed the bug that CJSON and CJavaScript might serialize float numbers into local-dependent strings (Qiang) -- Bug #1715: Fixed the bug that CActiveDataProvider.sort does not respect table alias set in the query criteria (Qiang) -- Bug #1718: Fixed the bug that Gii may fail if the error handler or user component is customized in the main application (Qiang) -- Bug #1719: Fixed the bug that CActiveForm->focus was not working if enableAjaxValidation was set to false (mdomba) -- Bug #1730: Fixed the bug that CDbConnection may attempt to use "SET NAMES" to set charset for Oracle DB (Qiang) -- Bug #1735: Fixed the bug that CGridView and CListView may fail to work in AJAX mode if setting pagerCssClass with multiple classes (Qiang) -- Bug #1748: Fixed the bug that CDbDataReader does not properly reset internal pointer when it has multiple rowsets (Qiang) -- Bug: Fixed the bug that some HTTP requests may cause a PHP notice complaining HTTP_HOST undefined in CHttpRequest (Qiang) -- Bug: Fixed a bug in CGridView JavaScript that would fail the deletion action in IE when ajaxUpdate is set false (Qiang) -- Bug: Fixed a bug that CFileCache may slow down performance when strlen is overloaded by mb_strlen (Qiang) -- Enh #202: Added support for console command actions and parameter binding (Qiang) -- Enh #970: Added CController::beforeRender() and CController::afterRender() (Qiang) -- Enh #1081: Refactored application global state management to allow loading and saving states explicitly for long-run tasks (Qiang) -- Enh #1126: CHtml can now properly render special HTML attributes, such as readonly, disabled, according to their boolean values (Qiang) -- Enh #1419: CMaskedTextField, CAutoComplete, CStarRating, CJuiDatePicker, CJuiAutoComplete and CJuiSliderInput now can be used with tabular input (Sam Dark) -- Enh #1450: Added support for theming widget views (Qiang) -- Enh #1481: Added support for autoloading namespaced classes (Qiang) -- Enh #1522: The attributes of CDetailView now support the property 'visible' (Y!!) -- Enh #1546: Fixed the bug that disabling behaviors did not detach behavior event handlers (Qiang) -- Enh #1555: Added support to allow unloading/resetting an application component by calling CModule::setComponent() (Qiang) -- Enh #1561: Enhanced Gii tooltip feature to allow disable tooltips for certain input fields (Qiang) -- Enh #1599: Refactored CMultiFileUpload by extending from CInputWidget (Qiang) -- Enh #1560: Removed potential circular references in relational AR queries (Qiang) -- Enh #1578: Added support to parse AM/PM by CDateTimeParser (Qiang) -- Enh #1583: Upgraded HTMLPurifier to v4.2.0 (Sam Dark) -- Enh #1591: Fixed yiic.bat to make sure it works even if the path of PHP executable contains spaces (Qiang) -- Enh #1594: Added CWebLogRoute::ignoreAjaxInFireBug to make sure ajax calls work when showInFireBug is set to true (mdomba) -- Enh #1596: Added 'not' property to CRangeValidator and CRegularExpressionValidator in order to support inversion of the validation logic (Y!!) -- Enh #1598: Fixed CHttpRequest::getUserAgent() to make sure it works even if HTTP_USER_AGENT is not defined (Qiang) -- Enh #1607: Added CDbCache::setDbConnection (Qiang) -- Enh #1611: Added support for using composite keys in CActiveDataProvider (Qiang) -- Enh #1618: Fixed CHttpRequest::getAcceptTypes() to make sure it works even if HTTP_ACCEPT is not defined (Y!!) -- Enh #1625: Replaced rand() with mt_rand() for generating random private keys (Qiang) -- Enh #1627: Added check if FreeType support is installed and enabled in GD (mdomba) -- Enh #1633: Added $defaults to CDateTimeParser::parse() to support more reasonable datetime parsing (Qiang) -- Enh #1641: Added PhpUnit 3.5.0RC1 and up support (Sam Dark) -- Enh #1644: Added CModel::onAfterConstruct event and allowed CModelBehavior to respond to this event (Qiang) -- Enh #1651: Added 'name' and 'model' properties to the attribute objects used in CActiveForm javascript code (Qiang) -- Enh #1658: Added CAssetManager::linkAssets to support publishing assets via symbolic links (Qiang) -- Enh #1659: Improved CHttpRequest::sendFile() and CWebService::renderWsdl() to make them more secure in case mbstring.func_overload is in effect (Qiang) -- Enh #1661: Added CActiveForm 'reset' event handler to reset validation errors if using CHtml::resetButton() (mdomba) -- Enh #1667: Added CDbCriteria::index to support indexing the AR query result array with the specified attribute values (Qiang) -- Enh #1668: Added validation to ensure PHP keywords be not used as class names (Qiang) -- Enh #1688: Refactored CDbMessageSource to allow easier extension (Qiang) -- Enh #1699: Added capability to remove duplicated script files registered for different positions in CClientScript (Qiang) -- Enh #1710: Upgraded treeview JavaScript to version 1.4.1 (mdomba) -- Enh #1711: JavaScript registered in POS_LOAD will now be put in jQuery window load event instead of the previous global window load event (Qiang) -- Enh #1742: Exposing the class map feature that was previously only available to core classes (Qiang) -- Enh #1738: Upgraded JQuery UI to 1.8.6 (Sam Dark) -- Enh #1740: Added CModelEvent::criteria so that in onBeforeEvent event, the query criteria can be accessed (Qiang) -- Enh #1753: Added method chaining support for CClientScript (Qiang) -- Enh: Added checking for empty keywords in addSearchCondition(), to prevent adding unnecessary conditions (mdomba) -- Enh: Added flushValues() method to the cache classes (Y!!) -- Enh: Added buttonset for CJuiButoon (sebas) -- Enh: Improved error handling to catch errors occurring in CApplication::end() (Qiang) -- Enh: Improved CHttpRequest::sendFile() to avoid timeout errors caused by long file downloading time (Qiang) -- Enh: Improved action parameter binding by detecting if a parameter requires array or not (Qiang) -- Enh: Added logging of DB query params in DB query profiling (Sam Dark, Vitaliy Stepanenko) -- Enh: Added CDbCommand::bindValues() (Qiang) -- Chg #1355: CHtml will no longer render null attributes in HTML tags (Qiang) -- Chg #1540: The 'name' option set in CCheckBoxColumn::checkBoxHtmlOptions will be kept as is without any change (Qiang) -- Chg #1678: The prompt and empty options used in CHtml methods will NOT be HTML-encoded anymore. (Qiang) -- Chg #1680: Upgraded jQuery to version 1.4.4 (Sam Dark) -- Chg #1756: Changed CGoogleApi::BOOTSTRAP_URL to CGoogleApi::$bootstrapUrl to allow customization (Qiang) -- Chg: The javascript files of CListView and CGridView are now registered at the end of the page (Qiang) -- Chg: Log filters will now be invoked only when there are some log messages available (Qiang) -- Chg: removed destructor from CDbCache, CDbAuthManager and CDbLogRoute to avoid potential DB connection issue (Qiang) -- New #1542: Added CTypedMap (Qiang) - - -Version 1.1.4 September 5, 2010 -------------------------------- -- Bug #698: Now you can get and modify criteria of the current query in beforeFind() event handler (Sam Dark) -- Bug #1031: Fixed the bug that the filters in CGridView does not work in IE (Qiang) -- Bug #1119: Added CUploadedFile::reset() to make it more test-friendly (Qiang) -- Bug #1176: Fixed the bug that CVarDumper doesn't highlight well strings with quotes (Qiang) -- Bug #1376: Fixed the bug that the timestamps displayed in Web application log may not be formatted properly (Qiang) -- Bug #1377: Fixed the bug that CStarRating did not work when not setting the model property (Qiang) -- Bug #1382: Fixed space removal in CDbCriteria::compare() (Sam Dark) -- Bug #1384: SET NAMES problem with MSSQL PDO Provider (Qiang) -- Bug #1390: AR may lose precision if a column is declared as unsigned int for MySQL database (Qiang) -- Bug #1404: CSecurityManager::validateData() fails when the data is an array (Qiang) -- Bug #1408: CDbAuthManager may throw exception when unserializing data from auth items in PHP 5.3 (Qiang) -- Bug #1432: AR find methods with JOIN in query criteria may populate AR objects with attribute values belonging to other tables (Qiang) -- Bug #1435: Table alias declared in scopes may be ignored when performing relational findByPk and findByAttributes queries (Qiang) -- Bug #1476: Fixed the bug that setting 'id' to be false will still render 'id' attribute in CHtml::radioButton and checkBox (Qiang) -- Bug #1455: CFormButtonElement generates wrong type for button tags (Qiang) -- Bug #1488: When using cookies with CJuiWidget jquery.cookie.js is not registered (sebas) -- Bug #1493: ShellCommand wouldn't process logs after exiting. (Qiang) -- Bug #1521: CUniqueValidator may incorrectly fail the validation of a non-PK column when updating both this column and the PK column (Qiang) -- Bug #1526: CFormInputElement by default should only show error if CForm::showErrorSummary is false (Qiang) -- Enh #954: Refactored CActiveRecord and CActiveFinder so that CActiveRecord::with() always returns the AR object itself (Qiang) -- Enh #1019: Improved CDataFormatter for formatting numeric weekdays (Qiang) -- Enh #1073: Allow dependencies to be set in constructor of CChainedCacheDependency. Also allow dependencies to be specified as configurations. (Qiang) -- Enh #1087: Allow CDbCriteria to be used as dynamic relational query options (Qiang) -- Enh #1104: Added argument "$" to jQuery block to prevent $ alias conflict (mdomba) -- Enh #1108: Added option to CFileHelper::getMimeType() to allow enable and disable falling back to extension-based MIME detection (Qiang) -- Enh #1120: Improved error handling in session write handler of CDbHttpSession (Qiang) -- Enh #1128: Improved error reporting when assets directory does not exist or is not writable (Qiang) -- Enh #1222: Added relations information to Gii generated model's PHPDoc (Sam Dark) -- Enh #1244: CActiveRecord::count() now respects GROUP-BY and HAVING settings (Qiang) -- Enh #1347: Added CPagination::validateCurrentPage (Qiang) -- Enh #1358: Enhanced the 'together' option of HAS_MANY/MANY_MANY relations so that setting it true will ensure the related table is joined with the primary table in a single SQL (Qiang) -- Enh #1359: Added CActiveRecord::countByAttributes (Qiang) -- Enh #1361: Added linkLabelWrapper, firstItemCssClass and lastItemCssClass to CMenu (Qiang) -- Enh #1366: Added CListView::itemsTagName (Qiang) -- Enh #1371: Improved js code in gii view templates to allow easier subclassing (Qiang) -- Enh #1392: Added CCaptchaAction::fixedVerifyCode (Qiang) -- Enh #1400: Enhanced CActiveRecord::getAttributeLabel() to support returning labels for related object's attribute (Qiang) -- Enh #1412: Yii::import() now throws an exception when trying to include nonexisting PHP file (Qiang) -- Enh #1414: Several enhancements to MSSQL driver used by AR (Qiang) -- Enh #1433: Added CMessageSource::forceTranslation (Qiang) -- Enh #1434: Added zii message translation in Italian (enrico.detoma) -- Enh #1440: CDbException does now provide a valid error code if possible (Y!!) -- Enh #1443: Added CCheckBoxColumn::checked to allow settings checked state for each CCheckBoxColumn row (Sam Dark) -- Enh #1444: Added CFilter::init() (Qiang) -- Enh #1449: Changed CDbCriteria's base class to be CComponent to better report configuration errors (Qiang) -- Enh #1461: Enhanced CEmailLogRoute to support additional email headers (Y!!) -- Enh #1471: CActiveForm AJAX validation should be cancelled when the form is already submitted (Qiang) -- Enh #1509: Improved CMarkdownParser so that it can be used in console mode (Qiang) -- Enh #1525: Added support to allow customizing 'name' attribute of checkboxes generated by CCheckBoxColumn (Qiang) -- Enh #1532: Exposed the serviceName and namespace properties of CWsdlGenerator (Qiang) -- Enh: Added CPortlet::hideOnEmpty property (Qiang) -- Enh: Added CValidator::safe to allow marking a validator as safe or unsafe (Qiang) -- Enh: Added CDbCacheDependency::params (Qiang) -- Enh: Added CUrlManager::addRules() (Qiang) -- Enh: Added support for using sqlsrv driver with MSSQL (Qiang) -- Enh: Added CActiveForm::focus to set input focus on page load (mdomba) -- Chg #1102: Added jQuery UI as a core client script package (Qiang) -- Chg #1309: CHttpRequest::getPathInfo() now always returns decoded results (Qiang) -- Chg #1494: CHtml::ajaxSubmitButton() will generate a submit button (Qiang) -- Chg #1515: CModel::onUnsafeAttribute() will be invoked only when $safeOnly is true when calling CModel::setAttributes (Qiang) -- Chg: Replaced jQuery live() with delegate() in CHtml-generated js code (Qiang) -- New: Upgraded JQuery UI to 1.8.4 (Sam Dark) -- New: Upgraded code highlighter: added sh and VBScript, fixed comments in CSS and hex numbers in JavaScript (Sam Dark) -- New: Added CSqlDataProvider and CArrayDataProvider (Qiang) -- New: Added support for automatic action parameter binding from $_GET (Qiang) - -Version 1.1.3 July 4, 2010 --------------------------- -- Bug #856: Logout doesn't work when CWebUser::identityCookie is configured and allowAutoLogin is set true (Qiang) -- Bug #1027: CButtonColumn->buttons is ignored (Sam Dark) -- Bug #1039: Table prefix feature did not work with PostgreSQL and AR (Qiang) -- Bug #1046: Fixed the bug that CDbFixtureManager did not properly initialize the fixture data (Qiang) -- Bug #0147: Fixed the bug that changing CAuthItem.description value would cause an exception when using CPhpAuthManager (Qiang) -- Bug #1050: Fixed the bug that filter conditions were prefilled with default values when using an AR model in CGridView (Qiang) -- Bug #1109: CActiveRecord::getRelated() did not refresh when setting the $refresh parameter to be true (Qiang) -- Bug #1142: Fixed the character encoding in polish translations (pawel.drylo) -- Bug #1149: CHtml::resolveName() does not work with multiple dimensional attributes (Qiang) -- Bug #1176: CVarDumper may omit some backslashes in the syntax-highlighted display (Qiang) -- Bug #1190: CLocale::getMonthNames may fail due to a typo (Qiang) -- Bug #1208: Unsigned integer column type was not handled correctly (Qiang) -- Bug #1213: Fixed the bug that skipOnError doesn't have effect on inline validators (Qiang) -- Bug #1226: CWebUser::autoRenewCookie does not handle the case when the user is already logged in (Qiang) -- Bug #1227: CActiveRecord::resetScope doesn't work with default scope (Sam Dark) -- Bug #1231: CPgsqlColumnSchema may incorrectly parse the default column when DB expression is used (Qiang) -- Bug #1241: DB search parameters should have special characters escaped (Qiang) -- Bug #1242: Fixed the bug that CGridView filtering and item deletion would not work when ajax-update is disabled (Qiang) -- Bug #1252: CJSON::encode() was not able to encode models and model arrays (Sam Dark) -- Bug #1262: Fixed the bug that CDbFixtureManager was unable to load fixture data if table prefix feature is used (Qiang) -- Bug #1292: CDateTimeParser::parse() did not honor the number of digits in the required format in some cases (Qiang) -- Bug #1293: Added tag to initial CAPTCHA image URL to avoid caching issue (Qiang) -- Bug #1295: CHtml::beginForm() would generate useless CSRF field when in GET mode (Qiang) -- Bug: Fixed AR memory leaks on PHP<5.3 (Sam Dark, parpaing) -- Enh #217: Added support to allow using related objects as selection values in CHtml (Qiang) -- Enh #663: Improved CSecurityManager to allow customizing the crypt/hash algorithms being used (Qiang) -- Enh #716: Improved the performance of statistical query in AR (Qiang) -- Enh #862: Enhanced CSort virtual attributes and support for related tables (Qiang) -- Enh #887: Relative URL's will be returned when using a parameterized hostname url rule that has the current hostinfo (Qiang) -- Enh #930: Updated CStarRating's jQuery plugin to v3.13, updated jQuery Metadata plugin (Sam Dark) -- Enh #952: Enhanced support for using defaultParams in CUrlManager (Qiang) -- Enh #1015: Added automatic column initialization when non-active data provider is used for CGridView (Qiang) -- Enh #1022: Added CMenu::activateItems (Qiang) -- Enh #1041: Added support to allow skinning pagers used in CGridView and CListView (Qiang) -- Enh #1043: Improved view resolution to support using themeable application views in a module (Qiang) -- Enh #1049: Enhanced label generation when using CDetailView with associative arrays (Qiang) -- Enh #1127: Added support to automatically generate maxlength attribute for text/password inputs based on model rules (Qiang) -- Enh #1151: Added support to generate grid column header based on attribute names (Qiang) -- Enh #1158: Added translations in Latvian (lafriks) -- Enh #1166: Added CActiveRecord::setOldPrimaryKey (Qiang) -- Enh #1174: AR's count() now generates more reasonable SQL statement when 'group' option is specified (Qiang) -- Enh #1179: Added CMultiFileUpload::file (Qiang) -- Enh #1180: Exposed several member variables in CClientScript to be protected (Qiang) -- Enh #1183: Added support to retrieve the currently active table alias in AR scopes (Qiang) -- Enh #1188: Removed exception message display in production mode when a DB connection fails to improve security (Qiang) -- Enh #1189: Added $loadedOnly parameter to CModule::getComponents() so that it can return all application components including unloaded ones (Qiang) -- Enh #1199: AR's count() method now respects the 'select' option in the query criteria (Qiang) -- Enh #1202: Added support for using anonymous functions as component property values (Qiang) -- Enh #1203: Gii now respects the newDirMode and newFileMode settings even when lower umask is set (Qiang) -- Enh #1210: Added support to generate proper labels for relational properties in CDetailView (Qiang) -- Enh #1225: Added 'firstError' option to CHtml::errorSummary() to support displaying only the first error message of each model attribute (Qiang) -- Enh #1232: Added CAuthManager::showErrors. When value is true Yii will turn on error_reporting for RBAC bizRules. False by default (Sam Dark) -- Enh #1239: CBreadcrumbs should have the 'Home' label translated (Qiang) -- Enh #1245: Optimized the implementation of checkAccess of CPhpAuthManager and CDbAuthManager (Qiang) -- Enh #1261: Added magicFile parameter to CFileHelper::getMimeType() and getMimeTypeByExtension() (Qiang) -- Enh #1268: Added isset and unset support to behavior properties in a component context (Qiang) -- Enh #1271: Added CWebUser::getFlashes() (Qiang) -- Enh #1276: Added CClientScript::coreScriptPosition to support customizing the insertion position of core scripts (Qiang) -- Enh #1278: Gii model generator will now respect the table prefix when determining which tables the models should be generated for (Qiang) -- Enh #1283: Added port and securePort properties to CHttpRequest (Qiang) -- Enh #1284: Added support to allow passing an AR finder as the first parameter of the constructor of CActiveDataProvider (Qiang) -- Enh #1286: Upgraded HTMLPurifier to v4.1.1 (Qiang) -- Enh #1289: Added support to allow using non-string values when calling CDbCriteria::compare() (Qiang) -- Enh #1290: Added cssClass to individual item in CDetailView (Qiang) -- Enh #1306: Hide log route outputs when no messages are collected after filtering (Qiang) -- Enh #1311: Added {page} and {pages} tokens to CBaseListView::summaryText (Qiang) -- Enh #1326: Added CBaseActiveRelation::join property (Qiang) -- Enh: CActiveRecord::beforeFind event is now triggered in all cases including related models with both lazy and eager loading (Sam Dark, creocoder) -- Enh: Added support for using array-typed model attributes in active methods in CHtml (Qiang) -- Enh: Added beforeValidate, afterValidate, beforeValidateAttribute and afterValidateAttribute options to CActiveForm (Qiang) -- Enh: Changed @var declarations to class @property declarations in gii and yiic shell model templates (Sam Dark) -- Enh: IDE code completion for CActiveRecord::attributes (Sam Dark) -- Enh: Added beforeLogin, afterLogin, beforeLogout and afterLogout to CWebUser (Qiang) -- Enh: Enhanced CSort::defaultOrder to allow using virtual attribute names (Qiang) -- Chg #1323: Conditions declared in scopes of the related AR classes will be put in the ON clause of the JOIN statement (Qiang) -- Chg: CAutoComplete is now deprecated (Sam Dark) -- New: Added CJuiButton (sebas) - -Version 1.1.2 May 2, 2010 -------------------------- -- Bug #676, 891: merging criterias with parameters is impossible (Sam Dark) -- Bug #1006: Setting CForm::attributes may cause exception (Qiang) -- Bug #1007: CActiveForm did not update the validation result correctly when change of one attribute affects the validity of another (Qiang) -- Bug #1014: CDataProvider was accessing non-existing property modelClass (Qiang) -- Bug #1021: Missing return in CAuthItem::removeChild (Sam Dark) -- Bug #1031: Added a temporary fix for dropdown filter in CGridView not working in IE (Qiang) -- Bug #1035: RBAC BizRule security violation (Sam Dark) -- Bug #1048: CAutoComplete conflicts with jQueryUI 1.8.x (Sam Dark) -- Bug #1115: Fixed the bug that using bigint with MySQL, PostgreSQL and SQL Server may lose precision (Qiang) -- Bug #1121: typo in CActiveRecord::setPrimaryKey() (Qiang) -- Bug #1136: Fixed wrong API call in CXCache::flush() (Qiang) -- Bug #1147: zii widget messages are not translated via Yii::t() (Sam Dark) -- Bug: Removed the debugging line in CActiveFinder that caused many-many relational query to fail if FKs are not defined (Qiang) -- Bug: Fixed the bug that doing performance profiling while turning on YII_TRACE_LEVEL would throw exception (Qiang) -- Bug: user was redirected to AJAX URLs after logging in (Sam Dark) -- Bug: RBAC rules with bizRule and caching enabled worked wrong (Sam Dark) -- Bug: CSort may cause exception if an invalid column is to be sorted (Qiang) -- Bug: AR count() does not generate correct SQL when distinct is set true in the criteria (Qiang) -- Bug: Relational AR query may complain column not well defined when the column select spans multiple lines (Qiang) -- Enh #943: dynamic AR relations (Sam Dark) -- Enh #946: Added a new parameter to CBaseController::widget() method to allow capturing the output of the widget (Qiang) -- Enh #977: Added CModel::getValidatorList() to allow adding/removing validation rules on the fly (Sam Dark, creocoder) -- Enh #1001: Added CActiveRecord::resetScope() that resets all scopes and criterias applied including default scope (Sam Dark) -- Enh #1009: Allow quoted columns in CDbCriteria::select when performing relational query (Qiang) -- Enh #1025: added 'uncheckValue' option to CHtml::radioButton() and CHtml::checkBox() (Jonah) -- Enh #1042: CForm __construct now uses setModel() instead of assigning _model directly (Sam Dark) -- Enh #1062: Added CDbCriteria::addBetweenCondition() (Sam Dark) -- Enh #1071: Optimized file copying in CUploadedFile::saveAs() (Sam Dark) -- Enh #1084: Added CLocale::getOrientation() to return character orientation information of a locale (Qiang) -- Enh #1091: Added support to allow using normal PHP views with special views recognized by the installed view renderer (Qiang) -- Enh #1093: CJSON now tries to use native PHP functions prior to use Yii implementation (Sam Dark) -- Enh #1140: Added CHttpSession::get() (Qiang) -- Enh #1156: Updated jQuery BBQ to 1.2.1 (Sam Dark) -- Enh #1282: Added support to configure widget default values in application configuration (Qiang) -- Enh: Improved IDE code completion for generated AR models (Sam Dark) -- Enh: CCaptchaAction now supports unlimited tests by setting its testLimit to be 0 (Qiang) -- Enh: Added $forceCopy parameter to CAssetManager::publish() (Qiang) -- Enh: CTypeValidator now supports checking array data (Qiang) -- Enh: Added CFileHelper::getExtension() (Qiang) -- Enh: Added CModule::hasModule() (Qiang) -- Enh: CFileValidator now works with php.ini's upload_max_filesize strings with K, G, k, m, g (Sam Dark) -- Enh: Enhanced CActiveForm to make it more robust in case some code error occurs when performing ajax validation (Qiang) -- Enh: CMaskedTextField's jquery.maskedinput updated to 1.2.2 (Sam Dark) -- Enh: Added support to allow CActiveRecord::getAttributes() to return custom attributes if required (Qiang) -- Chg #1118: CActiveRecord::refresh() now updates the record by directly updating the attributes array (Qiang) -- Chg #1125: Ability to use model metadata in behavior's attach() method (Sam Dark) -- Chg #1163: CLinkPager will enable first and last page buttons unless the current page is first or last. (Qiang) -- Chg #1164: CUrlRule will not throw 404 exception when unable to parse a URL under strict parsing mode (Qiang) -- New #1005: added CWinCache (Sam Dark) -- New #1013: Added CJuiAutoComplete (sebas) -- New: Added Ukrainian translations (Valeriy) -- New: Upgraded JQuery UI to 1.8.1 (Qiang) - -Version 1.1.1 March 14, 2010 ----------------------------- -- Bug #727: AR may lose precision for numbers of bigint type (Qiang) -- Bug #738: COciColumnSchema must return 'double' if precision and scale designators of NUMBER field are absent (Qiang) -- Bug #816: CUniqueValidator did not work with CFormModel (Qiang) -- Bug #823: typo in CLinkColumn about linkHtmlOptions (Qiang) -- Bug #839: typo in CFormatter about calling method_exists() (Qiang) -- Bug #865: CWidgetFactory didn't set the owner of the newly created widgets correctly (Qiang) -- Bug #869: CDateFormatter::formatTimeZone() may report error for certain locale data (Qiang) -- Bug #871: A module generated by the yiic module command did not use the application layout by default (Qiang) -- Bug #890: The 'alias' option set in default scope was ignored when performing an eager relational query (Qiang) -- Bug #932: CLocale::getWeekDayName() causes PHP error when requesting 'narrow' format data (Qiang) -- Bug #947: CTabView does not target tab links correctly when extra elements are put in the header (Qiang) -- Bug #957: CGettextPoFile should allow optional msgctxt (Qiang) -- Bug #967: CFormInputElement doesn't respect element-id if set (Sam Dark) -- Bug #988: COcSchema::quoteTableName() and quoteColumnName() should quote the names (Qiang) -- Bug #995: The 'alias' option set in default scope was ignored when some find methods in AR (Qiang) -- Bug #996: "yiic message" command generates incorrect message file name when used in a module context (Qiang) -- Bug #14 (zii): Added documentation about the "js:" prefix in CJuiSortable (sebas) -- Bug #18 (zii): Change the way CJuiDatePicker sets its language option (sebas) -- Bug #27 (zii): Fixed the issue that when CSRF is turned on, delete button doesn't work for CGridView (Qiang) -- Bug: Setting the 'with' option in criteria array doesn't trigger eager loading for AR (Qiang) -- Bug: CActiveRecord should update oldPrimaryKey after calling save() (Qiang) -- Bug: CForm renders invalid 'name' and 'type' attributes when used to generate nested forms (Qiang) -- Bug: Fixed the bug that beforeAjaxUpdate/afterAjaxUpdate of CGridView/CListView do not take effect. -- Bug: Fixed the bug that the names of URL parameters were not encoded (Qiang) -- Bug: CGridView and CListView may not register the needed CSS file for the pager (Qiang) -- Enh #38: Added support to allow CHtml links and buttons work in AJAX responses (Qiang) -- Enh #392: Added CStringValidator::encoding to support checking the length of multibyte strings (Qiang) -- Enh #686: Added CUrlManager::setBasePath() (Qiang) -- Enh #726: Added CDbExpression::params (Qiang) -- Enh #794: Added support to allow using * to select all primary table columns in relational AR query (Qiang) -- Enh #820: Added CAccessRule::message to allow customizing authorization error message (Qiang) -- Enh #826: Added CMenu::itemTemplate property and template option for each menu item (Qiang) -- Enh #857: Added $exit parameter to CController::forward() (Qiang) -- Enh #872: Added CFlexWidget::allowFullScreen (Qiang) -- Enh #888: Added CBaseUserIdentity::setPersistentStates (Sam Dark) -- Enh #898: Added support to allow defining global yiic commands (Qiang) -- Enh #912: Added CModel::onUnsafeAttribute() which will log a warning message when massively assigning unsafe attributes (Qiang) -- Enh #916: Added visible option to buttons in CButtonColumn (Qiang) -- Enh #918: Added support to show attribute name as the label when displaying an array using CDetailView (Qiang) -- Enh #936: Current module ID is no longer needed when calling CController::forward() (Qiang) -- Enh #941: AR now allows MANY_MANY relation to be specified more flexibly (Qiang) -- Enh #953: Added CGridView::hideHeader (Qiang) -- Enh #955: Added validateValue() to CUrlValidator and CEmailValidator (Qiang) -- Enh #971: Added CDbCriteria::addNotInCondition() (Sam Dark) -- Enh #992: Added 'data' option to CTabView.tabs property (Qiang) -- Enh: Refactored the blog demo to make use of the new CActiveForm and the filtering feature of CGridView (Qiang) -- Enh: Improved the code generated by yiic, including menu refactoring, filtering/search support, and using active form (Qiang) -- Enh: Improved CHtml::beginForm() to auto-generate hidden fields for a GET form whose action contains query string (Qiang) -- Enh: Added CDataProvider::setTotalItemCount() (Qiang) -- Enh: Added skipOnError property to built-in validators (Qiang) -- Enh: Added CDbConnection::initSQLs (Qiang) -- Enh: Added CHtml::refresh() (Qiang) -- Enh: Added CListView.loadingCssClass and CGridView.loadingCssClass (Qiang) -- Enh: Added filtering support for CGridView (Qiang) -- Enh: Added 'template' option to each attribute specification in CDetailView (Qiang) -- Chg #841: Changed CUrlManager::parsePathInfo() to non-static (Qiang) -- Chg #851: yiic tool no longer turns off E_NOTICE (Qiang) -- Chg #949: The init() method will be invoked after an AR instance is created by the find methods (Qiang) -- Chg #974: CComponent::evaluateExpression() no longer suppresses expression error (Qiang) -- Chg #978: CActiveRecord::afterSave() will now be invoked only when the saving is successful (Qiang) -- Chg: Upgraded jquery to version 1.4.2 (Qiang) -- Chg: CMenu will render the 'active' CSS class in the container tag of the link (Qiang) -- Chg: Set the default theme for JQuery UI widgets to be 'base' (Qiang) -- New: Added CActiveForm that performs model validations via AJAX (Qiang) -- New: Added 'form' command to the 'yiic shell' tool (Qiang) -- New: Upgraded JQuery UI to 1.8rc3 (Qiang) - -Version 1.1.0 January 10, 2010 ------------------------------- -- Bug #720: The new table prefix feature does not work with many-many relationship in AR (Qiang) -- Bug #735: CDbCriteria should save 'with' attribute when toArray() is called (Qiang) -- Chg #796: The alias name for the primary table in a relational AR query is changed to be 't' (Qiang) -- Chg: renamed CDetailView::model to be 'data'. renamed 'dataField' and 'dataExpression' to be 'name' and 'value' for grid view columns. (Qiang) -- Enh #656: Added support to indicate the size of enum column type for MySQL schema (sebas) -- Enh #767: Added CUrlRule::matchValue option to support creating URLs only when a rule's parameter value patterns are matched. (Qiang) -- Enh: Improved the default code generated by the yiic tool (Qiang) -- Enh: Refactored the blog demo (Qiang) -- New: Added CFormatter and CApplication::format (Qiang) -- New: Added CController::forward() and CController::route (Qiang) -- New: Added CTimestampBehavior to automatically set timestamps in AR (Jonah) -- New: Added CBaseMenu and CListMenu to aid in creating menus (Jonah) -- New: Added CJuiWidget (sebas, Qiang) -- New: Added CJuiInputWidget (sebas, Qiang) -- New: Added CJuiSlider (sebas, Qiang) -- New: Added CJuiSliderInput (sebas) -- New: Added CJuiAccordion (sebas, Qiang) -- New: Added CJuiProgressBar (sebas, Qiang) -- New: Added CJuiTabs (sebas, Qiang) -- New: Added CJuiDatePicker (sebas, Qiang) -- New: Added CJuiSortable (sebas) -- New: Added CJuiDialog (sebas) -- New: Added CJuiSelectable (sebas) -- New: Added CJuiDroppable (sebas) -- New: Added CJuiResizable (sebas) -- New: Added CJuiDraggable (sebas) -- New: Added CGridView (Qiang) -- New: Added CDetailView (Qiang) -- New: Added CListView (Qiang) -- New: Added CPortlet (Qiang) -- New: Added CBreadcrumbs (Qiang) - -Version 1.1rc December 13, 2009 -------------------------------- -- Bug #713: webapp command generates incorrect test bootstrap script (Qiang) -- Bug #724: the rememberMe attribute is not validated in generated webapp code (Qiang) -- Enh #666: Added support for auto-flushing log messages (Qiang) -- Enh #668: Nested forms generated by CForm will render attributes in the fieldset tag (Qiang) -- Enh #695: Upgraded HTML Purifier to 4.0.0 (Qiang) -- Enh #711: Added CWebUser::autoRenewCookie to support automatically renew cookie-based login (Qiang) -- Enh: Added CSS class for links generated by CSort to differentiate sorting directions (Qiang) -- Enh: Added CComponent::evaluateExpression() (Qiang) -- Enh: Added CPagination::offset and CPagination::limit (Qiang) -- Chg: CSort.multiSort is changed to sort by single attribute by default (Qiang) -- New: Added CDataProvider and CActiveDataProvider (Qiang) -- New: Updated I18N data to CLDR 1.7.1.2 and added support for stand-alone month and day names (Qiang) -- New: Added CDbCriteria::with to support eager loading via a criteria with this property (Qiang) - -Version 1.1b November 1, 2009 ------------------------------ -- Bug #611: When using relation name as table alias, it should be properly quoted to avoid name conflict (Qiang) -- Bug #642: CPgsqlSchema::findTableNames() should not include view names in the result (Qiang) -- Bug #652: Calling CFormElementCollection::remove() triggers a method-not-defined error (Qiang) -- Bug: Using CFileValidator causes an error about accessing a method of a non-object (Qiang) -- Enh #570: Improved CSort so that it can support sorting by complex expressions or compound attributes (Qiang) -- Enh #597: Added support for modifying primary key of an AR instance by calling save() directly (Qiang) -- Enh #622: Added support for using HTML button tags in form builder (Qiang) -- Enh: Added getFixtureData() and getFixtureRecord() to CDbTestCase and CWebTestCase (Qiang) -- Chg #574: session ID is no longer hashed for CDbHttpSession (Qiang) -- New #633: Added support to allow using customized locale data (Qiang) -- New: Added support for using table prefixes (Qiang) - -Version 1.1a October 1, 2009 ----------------------------- -- New #429: CFileValidator and CUploadedFile now accept multiple uploads (pestaa) -- New: Refactored scenario-based validation and massive assignments (Qiang) -- New: Added CDbSchema::checkIntegrity() and resetSequence() (Qiang) -- New: Added phpunit-based testing framework (Qiang) -- New: Added CForm and relevant classes to allow reusing form representation and rendering (Qiang) -- New: Added support for widget skins (Qiang) -- New: Added support for accessing behavior's properties via the component it is attached to (Qiang) -- Chg #433: Changed application and module parameter names to be case-sensitive (Qiang) -- Chg #556: CHtml::resolveName now supports array-typed properties (pestaa) -- Chg: Changed AR eager loading so that it generates and executes a single SQL statement by default (Qiang) -- Chg: Changed AR table aliasing so that it uses relation names as default table aliases (Qiang) -- Chg: Changed the default value of allowEmpty to be false for CCompareValidator. (Qiang) - -Version 1.0.12 March 14, 2010 ------------------------------ -- Bug #731: When using CWebService to generate WSDL, it may cause the error about Premature end of data in tag definitions in SoapClient (Qiang) -- Bug #740: CDbCriteria::addColumnCondition() should handle NULL parameter correctly (Qiang) -- Bug #742: CEmailValidator should allow upper case email addresses (Qiang) -- Bug #776: CWebUser may fail when unserializing invalid cookie data (Qiang) -- Bug #788: CHttpRequest.sendFile() gives incorrect content length when output_handler is enabled (Qiang) -- Bug #801: CCaptcha allows unlimited tests if the CAPTCHA image is not reloaded (Qiang) -- Bug #832: CJavaScript::quote() should also escape the sequence "encode($value); - - New: - - require_once(Yii::getPathOfAlias('system.vendors.Net_IDNA2.Net').DIRECTORY_SEPARATOR.'IDNA2.php'); - $idna=new Net_IDNA2(); - $result=$idna->encode($value); - -- CAPTCHA appearance has been changed. Non-free Duality.ttf font (used by CCaptchaAction) replaced by open/free - SpicyRice.ttf. New font is distributed under SIL Open Font License version 1.1. Do not forget to adjust font path - in case your application relies on Duality.ttf font file. - -- CSecurityManager::computeHMAC() method is now public and third parameter has been added. You must change signature - of this method in the extended child class to fit new circumstances. Otherwise an E_STRICT error will be issued. - -- CClientScript::registerScriptFile() and CClientScript::registerScript() methods signature changed. - Update your subclasses that override registerScriptFile() or registerScript() if any. - -- CActiveRecord::refreshMetaData() now clears meta data for all objects of the particular Active Record class. - Also CActiveRecord::refreshMetaData() will not create new meta data at once - new CActiveRecordMetaData instance - will be created on the first demand. - -- Oracle related note: in case you're using COciSchema::resetSequence() or CDbSchema::resetSequence() methods with - the Oracle database, keep in mind that its behavior has changed to be consistent with the same methods for - the other database management systems. Please refer to its documentation for more details and don't forget - to adjust your code respectively. - -- Signature of the CJuiInputWidget::resolveNameID() method has changed. If you're overriding this method you must - change your code to fit it. This method now accepts two parameters, it means you have to adjust code of the - overlapped descendant method signature as follows: - - protected function resolveNameID($nameProperty='name',$attributeProperty='attribute') - - And ancestor method call to: - - parent::resolveNameID($nameProperty,$attributeProperty); - -- In case you've used your own relation types extended from `CHasOneRelation` or - `CHasManyRelation` make sure you update these to reflect moving `through` property - from both these to `CActiveRelation`. - -- CSecurityManager::generateRandomKey() has been deprecated in favor of CSecurityManager::generateRandomString(). - Try not to use it anymore and avoid CSecurityManager::generateRandomKey() method in your code. - -Upgrading from v1.1.12 ----------------------- -- Both jQuery and jQueryUI were updated. Check [jQuery UI upgrade guide](http://jqueryui.com/upgrade-guide/1.9/) - and [jQuery release notes](http://blog.jquery.com/2012/08/09/jquery-1-8-released/). - -- We completed the behavior of CFormatter::sizeFormat() which has been introduced in Yii 1.1.11. If you are using it with your own translation file - you have to move your translations to the `yii` category in `yii.php` file which is handled by the application component `coreMessages`. - We also fixed the default translation strings to be correct English and apply to choice format, so you have to adjust your translation file keys. - -- Be sure to clean all your existing cache during the upgrade. The cache values from CDbCommand has been - changed to an array to be able to store false values (returned when no records are found). - -- Make sure all your event handlers in behaviors are public methods, as we are not supporting protected methods as event handlers anymore. - -- We fixed the calls to CActiveRecord::beforeFind() for consistency so that beforeFind() now always gets called for every relation - on eager loading even if the main query does not return a result. This has been the case for all CActiveRecord::find*()-methods - already but now also applies for findBySql() and findAllBySql(). - -- Criteria modification in CActiveRecord::beforeFind() did not apply to the query when model was loaded in a relational context. - Since version 1.1.13 changes to query criteria made in beforeFind() now also apply to the query when model is loaded in a relational context. - The main problem here is that you can not use the `t`-alias for your table anymore, you have to change your code to - use the table alias currently in use as this is different in relational context. - You can get that alias by calling `$this->getTableAlias();` in your active record class - or `$this->owner->getTableAlias()` in behavior context. - - Example: - $criteria->condition = 't.myfield = 1'; - You need to change that to: - $alias = $this->owner->getTableAlias(); - $criteria->condition = $alias.'.myfield = 1'; - -- Make sure you are using `CCaptcha::checkRequirements()` method for checking whether CAPTCHA could be rendered successfully in your environment. - `extension_loaded('gd')` expression is not enough and wrong because CAPTCHA could be rendered via ImageMagick with fallback to GD since 1.1.13 - (thus checking code is not simple as it seems). - -- In case you're using MSSQL driver make sure authentication credentials you use have permissions to use - `sys.extended_properties` system view. This is critical for retrieving additional metadata on tables. - -Upgrading from v1.1.11 ----------------------- -- Changes in CCookieCollection::add() (introduced in 1.1.11) were reverted as they were triggering E_STRICT on some old PHP-versions - If your application relies on these newly added changes. You should change your code from - - $cookies->add(new CHttpCookie($name, $value)); - to - $cookies[$name] = new CHttpCookie($name, $value); - - -- CActiveRecord::resetScope() method signature changed. Please update your subclasses that override resetScope() if any. - -Upgrading from v1.1.10 ----------------------- -- API of public method CConsoleCommand::confirm() changed. If you are overriding this method make sure to update your code. - The method now has a 2nd parameter for the default value which will be used if no selection is made, so you have to - adjust the signature to fit - - public function confirm($message,$default=false) - - and the parent call to - - parent::confirm($message,$default); - -- API of protected method CConsoleCommand::afterAction() changed, if you are overriding this method make sure to update your code. - method now has a 3rd parameter for application exit code, so you have to adjust the signature to fit - - protected function afterAction($action,$params,$exitCode=0) - - and the parent call to - - parent::afterAction($action,$params,$exitCode); - -- CDateFormat::format() now will return null if the parameter $time is null. Previously it would return 1/1/1970. - -- If you are using CJavaScript::encode in your application with parameter coming - from user input, set second argument to true: - - CJavaScript::encode($userInput, true); - - It will disable prefixing parameters with "js:". If you need to pass JavaScript - expression it's now preferrable to wrap these with CJavaScriptExpression: - - CJavaScript::encode(new CJavaScriptExpression('alert("Yii!");'), true); - - Note that second "safe" parameter doesn't affect CJavaScriptExpression in any way. - - -Upgrading from v1.1.9 ---------------------- -- Previously xSendFile() was returning false if the file was not found. - This has been removed to allow relative file paths. If you are relying on this check, - you will need to do it manually before calling xSendFile(). - -Upgrading from v1.1.8 ---------------------- -- CConfiguration::createObject, CController::paginate and CHtml::getActiveId deprecated since 1.0.x were removed. Use - Yii::createComponent, new CPagination directly and CHtml::activeId respectively. - -- In CErrorHandler::handleException() the checking for ajax call has been removed - as it was preventing to customize the display of the exception during an ajax call. - -- Previously in case of validation error the CSS "error" class was not added to the row container at all when - checkBoxList or radioButtonList where used. This is fixed now and proper "error" CSS class is added to the row - container but in case of validation error, all labels from the list are shown in red because of the CSS rule. - - To fix this and display only the attribute label in red: - - in /css/form.css the line: - - div.form div.error label - - should be changed to - - div.form div.error label:first-child - -- If you've used "through" ActiveRecord option in your relation definitions it's good to update code as shown below. - Old style of defining this option still works but is now deprecated. - - Change - -~~~ -class Group extends CActiveRecord -{ - public function relations() - { - return array( - 'roles'=>array(self::HAS_MANY,'Role','group_id'), - 'users'=>array(self::HAS_MANY,'User','user_id','through'=>'roles'), - ); - } -} -~~~ - - to - -~~~ -class Group extends CActiveRecord -{ - public function relations() - { - return array( - 'roles'=>array(self::HAS_MANY,'Role','group_id'), - 'users'=>array(self::HAS_MANY,'User',array('user_id'=>'id'),'through'=>'roles'), - ); - } -} -~~~ - - -Upgrading from v1.1.7 ---------------------- -- CDbAuthManager will now quote columns and tables referenced in its SQL code. - If your auth tables were created in a case-insensitive fashion (e.g. on PostgreSQL) - while your DBMS is case-sensitive, this change may cause DB query errors. - To fix this issue, you will have to rename the table names and columns, or re-create - the auth tables by following the SQL code given in framework/web/auth/*.sql. -- jQuery was upgraded to 1.6.1. Check your client side code and if you have issues consider - downgrading to 1.5.1 or 1.4.4. - - -Upgrading from v1.1.6 ---------------------- -- Make sure you are using latest stable PHPUnit 3.5 if you are using unit tests. - - -Upgrading from v1.1.5 ---------------------- -- In CActiveRecord::relations(), if a relation involves composite foreign keys, the foreign key - columns must be separated by commas now. Previously, the columns can be separated by either - commas or spaces. If your mode code are generated by Gii or yiic shell, you do not need to - worry about this. -- CLDR data was updated to a newest available version so data formats, month - names and other regional data can be changed. - - -Upgrading from v1.1.4 ---------------------- -- CHtml will no longer render null attributes for HTML tags. This means if $htmlOptions is - array('class'=>null), it will no longer render the 'class' attribute in the HTML tag. - it would render the class attribute as class="". We expect this will not cause much trouble - in upgrading. However, in case problems happen, you may set the attribute to be an empty string - to solve them. - -- Now by default CWebLogRoute does not render logs in FireBug for ajax calls. - To get logs rendered for ajax calls in FireBug set CWebLogRoute::ignoreAjaxInFireBug to false - -- The implementation of CCache::flush() was changed a little. Child classes should now implement - a flushValues() method. If you use any custom cache class with flush functionality, you should - rename the flush method accordingly. - -- The prompt and empty options used in CHtml methods will NOT be HTML-encoded anymore. It will now - always convert ">" and "<" into ">" and "lt;", respectively. This should be sufficient in most - cases. But if your application uses some other special characters, or if you allow user inputs - to be used as prompt and empty text labels, please call CHtml::encode() explicitly on these option - values. - - -Upgrading from v1.1.3 ---------------------- -- Zii was merged into Yii so if you are using SVN to keep framework - up to date, you'll need to delete 'framework/zii' and then update it. - -Upgrading from v1.1.2 ---------------------- -- When using the skin feature, you now need to explicitly configure - the 'enableSkin' property of 'widgetFactory' application component - to be true. Also, if you have configured the 'widgets' property, you - should rename it to be 'skinnableWidgets'. These changes are due to - the introduction of the global widget customization feature. - Please see the guide (the "Theming" section) for more details. - -- CAutoComplete is now deprecated and will be removed in Yii 1.2. Consider - using CJuiAutoComplete. - -- Now it's not possible to reuse CActiveFinder. So if you have code like this: - - $finder = Post::model()->with('comments'); - $posts1 = $finder->findAll(); - … - $posts2 = $finder->findAll(); - - you should rewrite it to: - - $posts1 = Post::model()->with('comments'); - … - $posts2 = Post::model()->with('comments'); - -- The 'condition' declared in the scopes of the related AR classes will now - be put in the 'ON' clause of the JOIN statement when performing relational AR queries. - - -Upgrading from v1.1.1 ---------------------- - -Upgrading from v1.1.0 ---------------------- -- CHtml::beginForm() will automatically generate hidden fields to represent - the parameters in the query string when the form uses GET method. - To avoid submitting duplicated query parameters, you may use createUrl() - to explicitly specify the action of the form. You may also need to remove - the hidden fields that you previously render for the same purpose. - -- The code generated by yiic tool is changed. If you are using yiic tool - to generate new CRUD code on a previously generated skeleton, you are recommended - to run "yiic webapp" again to re-generate the skeleton. Make sure you backup - your work before you do this. - -- CMenu now renders the 'active' CSS class for the 'li' tag instead of the - hyperlink tag. You should adjust your CSS code accordingly if you use CMenu - in your application. - -- CUrlManager::parsePathInfo() is changed to be non-static. If you override - this method or your existing code calls this method directly (neither is common), - you need to change your code accordingly. - -- CController::forward() will exit the application by default now. If you want - to keep the old behavior, you may pass false as the second parameter. - -- The jQuery copy included in the framework has been upgraded to version 1.4.2. - This may cause some incompatibility problems to your existing jQuery code or plugins. - If you want to keep using version 1.3.2, you may do so by configuring - CClientScript::scriptMap property. - -- The default theme for JQuery UI widgets was changed from 'smoothness' to 'base'. - If you are using 'smoothness', you will need to manually download this theme from - jqueryui.com and configure the 'theme' property of the JQuery UI widgets accordingly. - - -Upgrading from v1.1rc ---------------------- -- CRudColumn is renamed as CButtonColumn -- CDataColumn.dataField and dataExpression are renamed as name and value, respectively -- The alias name for the primary table in an AR query is fixed to be 't' - -Upgrading from v1.1b --------------------- - -Upgrading from v1.1a --------------------- -- CSort::attributes is changed. Now the array keys refer to attribute names - and array values refer to virtual attribute definitions. Please read the - API documentation for this property to learn more details. This change will - affect your code only when you explicitly specify this property. - -Upgrading from v1.0.x ---------------------- -- Application and module parameter names are changed to be case-sensitive. - In 1.0.x, they are case-insensitive. - -- For tabular input, using Field[$i] is not valid anymore. Attribute names - should look like [$i]Field in order to support array-typed fields - (e.g. [$i]Field[$index]). - -- Please read the Guide for further details on how to upgrade from v1.0.x to v1.1. - -Upgrading from v1.0.12 ----------------------- - -Upgrading from v1.0.11 ----------------------- - -Upgrading from v1.0.10 ----------------------- - -Upgrading from v1.0.9 ---------------------- - -Upgrading from v1.0.8 ---------------------- -- ActiveRecord lazy loading is changed for optimization purpose. Previously, - when lazy loading occurs, the related table will be joined with the primary - table. Now, the related table will be queried without joining the primary table. - As a result, if you are using lazy loading and the corresponding relation - declaration includes reference to the primary table, the query will fail. - To fix this problem, please specify the lazy loading query options with the - actual primary table column values. - -Upgrading from v1.0.7 ---------------------- -- A directory imported using Yii::import() will have precedence over - any existing include paths. For example, if we import 'application.models.*', - then the corresponding directory will be searched before any other - existing include paths. This also means, a directory imported later will - have precedence over directories imported earlier. Previously, this order - was reversed. This change may affect you if you have several classes with - the same name and they are imported via different directories. You will need - to adjust the import order of these directories to make sure your existing - applications are not broken due to this change. - - -Upgrading from v1.0.6 ---------------------- -- Default named scope will no longer be applied to INSERT, UPDATE and - DELETE queries. It is only applied to SELECT queries. You should be aware - of this change if you override CActiveRecord::defaultScope() in your code. - -- The signature of CWebUser::logout() is changed. If you override this method, - you will need to modify your method declaration accordingly. - - -Upgrading from v1.0.5 ---------------------- - - -Upgrading from v1.0.4 ---------------------- -- CWebUser::checkAccess() takes an additional parameter to allow caching - the access check results. If you override this method, you will need to - modify your method declaration accordingly. Because the new parameter will - enable caching the access check results by default, please double check - your code containing this method call to make sure the behavior is as expected. - -- CDateParser has been renamed to CDateTimeParser - - -Upgrading from v1.0.3 ---------------------- -- The signature of CWebModule::init() is modified. Its parameter is removed. - If your application uses modules, you have to modify your module class - files accordingly. - - -Upgrading from v1.0.2 ---------------------- -- Controllers that are organized in subdirectories are now referenced - using the ID format "path/to/xyz". Previously it was "path.to.xyz". - If you use "path.to.xyz" in your application, you have to modify it - to "path/to/xyz". - -- CHtml::coreScript() is removed. If you used this in your application, - please use the following alternative: - - Yii::app()->clientScript->registerCoreScript($name); - - -Upgrading from v1.0.1 ---------------------- -- Due to the introduction of the scenario-based massive assignment feature, - we removed CActiveRecord::protectedAttributes(). Please use safeAttributes() - to specify which attributes are safe to be massively assigned. - - For more details about scenario-based assignment and validation, - please read the following tutorial section: - - http://www.yiiframework.com/doc/guide/form.model#securing-attribute-assignments - -- The signature of CModel::validate() has been changed to: - - CModel::validate($scenario='', $attributes=null) - - That is, the order of the two parameters has been swapped. If your application - contains code that invokes the validate() method (of either a CFormModel - or a CActiveRecord object) with some parameter, please make sure you fix - the parameter order. - -- The usage of CActiveRecord::with() and the 'with' option in relations - has been changed. In order to query child relations, we should specify the 'with' - parameter/option like the following now: - - Post::model()->with(array('comments', 'author.profile'))->findAll(); - - Previously, this should be written as: - - Post::model()->with(array('comments', 'author'=>'profile'))->findAll(); - - If your code does not involve child relations (like 'profile' in the above), - nothing needs to be changed. - - This change has been introduced in order to support dynamic relational - query options. For example, we can specify that comments be sorted in - descending order (assuming in the relations() method it is specified as - ascending order): - - Post::model()->with(array( - 'comments'=>array('order'=>'createTime DESC'), - 'author.profile', - ))->findAll(); - - -Upgrading from v1.0.0 ---------------------- -- An $scenario parameter is added to both CModel::beforeValidate() and afterValidate(). - If you override these methods in your child classes (form models, AR classes), - make sure you change the method signature accordingly. diff --git a/framework/YiiBase.php b/framework/YiiBase.php index b9e27b7091c..00694369219 100644 --- a/framework/YiiBase.php +++ b/framework/YiiBase.php @@ -80,7 +80,7 @@ class YiiBase */ public static function getVersion() { - return '1.1.15'; + return '1.1.16'; } /** @@ -299,7 +299,8 @@ public static function import($alias,$forceInclude=false) if(($pos=strrpos($alias,'.'))===false) // a simple class name { - if($forceInclude && self::autoload($alias)) + // try to autoload the class with an autoloader if $forceInclude is true + if($forceInclude && (Yii::autoload($alias,true) || class_exists($alias,true))) self::$_imports[$alias]=$alias; return $alias; } @@ -392,15 +393,19 @@ public static function setPathOfAlias($alias,$path) * Class autoload loader. * This method is provided to be invoked within an __autoload() magic method. * @param string $className class name + * @param bool $classMapOnly whether to load classes via classmap only * @return boolean whether the class has been loaded successfully + * @throws CException When class name does not match class file in debug mode. */ - public static function autoload($className) + public static function autoload($className,$classMapOnly=false) { // use include so that the error PHP file may appear if(isset(self::$classMap[$className])) include(self::$classMap[$className]); elseif(isset(self::$_coreClasses[$className])) include(YII_PATH.self::$_coreClasses[$className]); + elseif($classMapOnly) + return false; else { // include class file relying on include_path @@ -709,6 +714,9 @@ public static function registerAutoloader($callback, $append=false) 'CDbExpression' => '/db/schema/CDbExpression.php', 'CDbSchema' => '/db/schema/CDbSchema.php', 'CDbTableSchema' => '/db/schema/CDbTableSchema.php', + 'CCubridColumnSchema' => '/db/schema/cubrid/CCubridColumnSchema.php', + 'CCubridSchema' => '/db/schema/cubrid/CCubridSchema.php', + 'CCubridTableSchema' => '/db/schema/cubrid/CCubridTableSchema.php', 'CMssqlColumnSchema' => '/db/schema/mssql/CMssqlColumnSchema.php', 'CMssqlCommandBuilder' => '/db/schema/mssql/CMssqlCommandBuilder.php', 'CMssqlPdoAdapter' => '/db/schema/mssql/CMssqlPdoAdapter.php', @@ -750,6 +758,7 @@ public static function registerAutoloader($callback, $append=false) 'CLogRouter' => '/logging/CLogRouter.php', 'CLogger' => '/logging/CLogger.php', 'CProfileLogRoute' => '/logging/CProfileLogRoute.php', + 'CSysLogRoute' => '/logging/CSysLogRoute.php', 'CWebLogRoute' => '/logging/CWebLogRoute.php', 'CDateTimeParser' => '/utils/CDateTimeParser.php', 'CFileHelper' => '/utils/CFileHelper.php', diff --git a/framework/base/CApplication.php b/framework/base/CApplication.php index 2264eb7ba5e..bb2e409c8e9 100644 --- a/framework/base/CApplication.php +++ b/framework/base/CApplication.php @@ -403,8 +403,7 @@ public function findLocalizedFile($srcFile,$srcLanguage=null,$language=null) */ public function getLocale($localeID=null) { - $class=$this->localeClass; - return $class::getInstance($localeID===null?$this->getLanguage():$localeID); + return call_user_func_array(array($this->localeClass, 'getInstance'),array($localeID===null?$this->getLanguage():$localeID)); } /** @@ -414,8 +413,10 @@ public function getLocale($localeID=null) */ public function getLocaleDataPath() { - $class=$this->localeClass; - return $class::$dataPath===null ? Yii::getPathOfAlias('system.i18n.data') : $class::$dataPath; + $vars=get_class_vars($this->localeClass); + if(empty($vars['dataPath'])) + return Yii::getPathOfAlias('system.i18n.data'); + return $vars['dataPath']; } /** @@ -425,8 +426,8 @@ public function getLocaleDataPath() */ public function setLocaleDataPath($value) { - $class=$this->localeClass; - $class::$dataPath=$value; + $property=new ReflectionProperty($this->localeClass,'dataPath'); + $property->setValue($value); } /** diff --git a/framework/base/CComponent.php b/framework/base/CComponent.php index 103be3538a5..13d81f5a30b 100644 --- a/framework/base/CComponent.php +++ b/framework/base/CComponent.php @@ -261,7 +261,7 @@ public function __call($name,$parameters) return call_user_func_array(array($object,$name),$parameters); } } - if(class_exists('Closure', false) && $this->canGetProperty($name) && $this->$name instanceof Closure) + if(class_exists('Closure', false) && ($this->canGetProperty($name) || property_exists($this, $name)) && $this->$name instanceof Closure) return call_user_func_array($this->$name, $parameters); throw new CException(Yii::t('yii','{class} and its behaviors do not have a method or closure named "{name}".', array('{class}'=>get_class($this), '{name}'=>$name))); @@ -499,7 +499,7 @@ public function getEventHandlers($name) * $component->getEventHandlers($eventName)->add($eventHandler); * * - * Using {@link getEventHandlers}, one can also specify the excution order + * Using {@link getEventHandlers}, one can also specify the execution order * of multiple handlers attaching to the same event. For example: *
 	 * $component->getEventHandlers($eventName)->insertAt(0,$eventHandler);
diff --git a/framework/base/CErrorHandler.php b/framework/base/CErrorHandler.php
index 3ece1b2c1b1..f6b0ca54121 100644
--- a/framework/base/CErrorHandler.php
+++ b/framework/base/CErrorHandler.php
@@ -48,6 +48,7 @@
  * {@link CApplication::getErrorHandler()}.
  *
  * @property array $error The error details. Null if there is no error.
+ * @property Exception|null $exception exception instance. Null if there is no exception.
  *
  * @author Qiang Xue 
  * @package system.base
@@ -82,6 +83,7 @@ class CErrorHandler extends CApplicationComponent
 	public $errorAction;
 
 	private $_error;
+	private $_exception;
 
 	/**
 	 * Handles the exception/error event.
@@ -150,6 +152,15 @@ public function getError()
 		return $this->_error;
 	}
 
+	/**
+	 * Returns the instance of the exception that is currently being handled.
+	 * @return Exception|null exception instance. Null if there is no exception.
+	 */
+	public function getException()
+	{
+		return $this->_exception;
+	}
+
 	/**
 	 * Handles the exception.
 	 * @param Exception $exception the exception captured
@@ -186,6 +197,7 @@ protected function handleException($exception)
 				unset($trace[$i]['object']);
 			}
 
+			$this->_exception=$exception;
 			$this->_error=$data=array(
 				'code'=>($exception instanceof CHttpException)?$exception->statusCode:500,
 				'type'=>get_class($exception),
@@ -198,17 +210,12 @@ protected function handleException($exception)
 			);
 
 			if(!headers_sent())
-				header("HTTP/1.0 {$data['code']} ".$this->getHttpHeader($data['code'], get_class($exception)));
-
-			if($exception instanceof CHttpException || !YII_DEBUG)
-				$this->render('error',$data);
-			else
 			{
-				if($this->isAjaxRequest())
-					$app->displayException($exception);
-				else
-					$this->render('exception',$data);
+				$httpVersion=Yii::app()->request->getHttpVersion();
+				header("HTTP/$httpVersion {$data['code']} ".$this->getHttpHeader($data['code'], get_class($exception)));
 			}
+
+			$this->renderException();
 		}
 		else
 			$app->displayException($exception);
@@ -270,7 +277,8 @@ protected function handleError($event)
 				default:
 					$type = 'PHP error';
 			}
-			$this->_error=$data=array(
+			$this->_exception=null;
+			$this->_error=array(
 				'code'=>500,
 				'type'=>$type,
 				'message'=>$event->message,
@@ -280,13 +288,12 @@ protected function handleError($event)
 				'traces'=>$trace,
 			);
 			if(!headers_sent())
-				header("HTTP/1.0 500 Internal Server Error");
-			if($this->isAjaxRequest())
-				$app->displayError($event->code,$event->message,$event->file,$event->line);
-			elseif(YII_DEBUG)
-				$this->render('exception',$data);
-			else
-				$this->render('error',$data);
+			{
+				$httpVersion=Yii::app()->request->getHttpVersion();
+				header("HTTP/$httpVersion 500 Internal Server Error");
+			}
+
+			$this->renderError();
 		}
 		else
 			$app->displayError($event->code,$event->message,$event->file,$event->line);
@@ -327,15 +334,47 @@ protected function getExactTrace($exception)
 	 */
 	protected function render($view,$data)
 	{
-		if($view==='error' && $this->errorAction!==null)
+		$data['version']=$this->getVersionInfo();
+		$data['time']=time();
+		$data['admin']=$this->adminInfo;
+		include($this->getViewFile($view,$data['code']));
+	}
+
+	/**
+	 * Renders the exception information.
+	 * This method will display information from current {@link error} value.
+	 */
+	protected function renderException()
+	{
+		$exception=$this->getException();
+		if($exception instanceof CHttpException || !YII_DEBUG)
+			$this->renderError();
+		else
+		{
+			if($this->isAjaxRequest())
+				Yii::app()->displayException($exception);
+			else
+				$this->render('exception',$this->getError());
+		}
+	}
+
+	/**
+	 * Renders the current error information.
+	 * This method will display information from current {@link error} value.
+	 */
+	protected function renderError()
+	{
+		if($this->errorAction!==null)
 			Yii::app()->runController($this->errorAction);
 		else
 		{
-			// additional information to be passed to view
-			$data['version']=$this->getVersionInfo();
-			$data['time']=time();
-			$data['admin']=$this->adminInfo;
-			include($this->getViewFile($view,$data['code']));
+			$data=$this->getError();
+			if($this->isAjaxRequest())
+				Yii::app()->displayError($data['code'],$data['message'],$data['file'],$data['line']);
+			elseif(YII_DEBUG)
+				$this->render('exception',$data);
+			else
+				$this->render('error',$data);
 		}
 	}
 
diff --git a/framework/base/CModel.php b/framework/base/CModel.php
index 4eb25897350..3bdef0466d1 100644
--- a/framework/base/CModel.php
+++ b/framework/base/CModel.php
@@ -64,7 +64,7 @@ abstract public function attributeNames();
 	 * 
  • except: this specifies the scenarios when the validation rule should not be performed. * Separate different scenarios with commas. Please see {@link scenario} for more details about this option.
  • *
  • additional parameters are used to initialize the corresponding validator properties. - * Please refer to individal validator class API for possible properties.
  • + * Please refer to individual validator class API for possible properties. * * * The following are some examples: diff --git a/framework/base/CModule.php b/framework/base/CModule.php index 30b3fbe2f09..b81b2bd4190 100644 --- a/framework/base/CModule.php +++ b/framework/base/CModule.php @@ -332,8 +332,13 @@ public function getModules() * You may also enable or disable a module by specifying the 'enabled' option in the configuration. * * @param array $modules module configurations. + * @param boolean $merge whether to merge the new module configuration + * with the existing one. Defaults to true, meaning the previously registered + * module configuration with the same ID will be merged with the new configuration. + * If set to false, the existing configuration will be replaced completely. + * This parameter is available since 1.1.16. */ - public function setModules($modules) + public function setModules($modules,$merge=true) { foreach($modules as $id=>$module) { @@ -342,16 +347,18 @@ public function setModules($modules) $id=$module; $module=array(); } - if(!isset($module['class'])) - { - Yii::setPathOfAlias($id,$this->getModulePath().DIRECTORY_SEPARATOR.$id); - $module['class']=$id.'.'.ucfirst($id).'Module'; - } - - if(isset($this->_moduleConfig[$id])) + if(isset($this->_moduleConfig[$id]) && $merge) $this->_moduleConfig[$id]=CMap::mergeArray($this->_moduleConfig[$id],$module); else + { + if(!isset($module['class'])) + { + if (Yii::getPathOfAlias($id)===false) + Yii::setPathOfAlias($id,$this->getModulePath().DIRECTORY_SEPARATOR.$id); + $module['class']=$id.'.'.ucfirst($id).'Module'; + } $this->_moduleConfig[$id]=$module; + } } } diff --git a/framework/base/CSecurityManager.php b/framework/base/CSecurityManager.php index e4c4640257e..a285ceb0048 100644 --- a/framework/base/CSecurityManager.php +++ b/framework/base/CSecurityManager.php @@ -48,6 +48,20 @@ class CSecurityManager extends CApplicationComponent const STATE_VALIDATION_KEY='Yii.CSecurityManager.validationkey'; const STATE_ENCRYPTION_KEY='Yii.CSecurityManager.encryptionkey'; + /** + * @var array known minimum lengths per encryption algorithm + */ + protected static $encryptionKeyMinimumLengths=array( + 'blowfish'=>4, + 'arcfour'=>5, + 'rc2'=>5, + ); + + /** + * @var boolean if encryption key should be validated + */ + public $validateEncryptionKey=true; + /** * @var string the name of the hashing algorithm to be used by {@link computeHMAC}. * See {@link http://php.net/manual/en/function.hash-algos.php hash-algos} for the list of possible @@ -62,12 +76,20 @@ class CSecurityManager extends CApplicationComponent * This will be passed as the first parameter to {@link http://php.net/manual/en/function.mcrypt-module-open.php mcrypt_module_open}. * * This property can also be configured as an array. In this case, the array elements will be passed in order - * as parameters to mcrypt_module_open. For example, array('rijndael-256', '', 'ofb', ''). + * as parameters to mcrypt_module_open. For example, array('rijndael-128', '', 'ofb', ''). + * + * Defaults to AES + * + * Note: MCRYPT_RIJNDAEL_192 and MCRYPT_RIJNDAEL_256 are *not* AES-192 and AES-256. The numbers of the MCRYPT_RIJNDAEL + * constants refer to the block size, whereas the numbers of the AES variants refer to the key length. AES is Rijndael + * with a block size of 128 bits and a key length of 128 bits, 192 bits or 256 bits. So to use AES in Mcrypt, you need + * MCRYPT_RIJNDAEL_128 and a key with 16 bytes (AES-128), 24 bytes (AES-192) or 32 bytes (AES-256). The other two + * Rijndael variants in Mcrypt should be avoided, because they're not standardized and have been analyzed much less + * than AES. * - * Defaults to 'des', meaning using DES crypt algorithm. * @since 1.1.3 */ - public $cryptAlgorithm='des'; + public $cryptAlgorithm='rijndael-128'; private $_validationKey; private $_encryptionKey; @@ -158,10 +180,8 @@ public function getEncryptionKey() */ public function setEncryptionKey($value) { - if(!empty($value)) - $this->_encryptionKey=$value; - else - throw new CException(Yii::t('yii','CSecurityManager.encryptionKey cannot be empty.')); + $this->validateEncryptionKey($value); + $this->_encryptionKey=$value; } /** @@ -191,12 +211,14 @@ public function setValidation($value) * @param string $data data to be encrypted. * @param string $key the decryption key. This defaults to null, meaning using {@link getEncryptionKey EncryptionKey}. * @return string the encrypted data - * @throws CException if PHP Mcrypt extension is not loaded + * @throws CException if PHP Mcrypt extension is not loaded or key is invalid */ public function encrypt($data,$key=null) { + if($key===null) + $key=$this->getEncryptionKey(); + $this->validateEncryptionKey($key); $module=$this->openCryptModule(); - $key=$this->substr($key===null ? md5($this->getEncryptionKey()) : $key,0,mcrypt_enc_get_key_size($module)); srand(); $iv=mcrypt_create_iv(mcrypt_enc_get_iv_size($module), MCRYPT_RAND); mcrypt_generic_init($module,$key,$iv); @@ -211,12 +233,14 @@ public function encrypt($data,$key=null) * @param string $data data to be decrypted. * @param string $key the decryption key. This defaults to null, meaning using {@link getEncryptionKey EncryptionKey}. * @return string the decrypted data - * @throws CException if PHP Mcrypt extension is not loaded + * @throws CException if PHP Mcrypt extension is not loaded or key is invalid */ public function decrypt($data,$key=null) { + if($key===null) + $key=$this->getEncryptionKey(); + $this->validateEncryptionKey($key); $module=$this->openCryptModule(); - $key=$this->substr($key===null ? md5($this->getEncryptionKey()) : $key,0,mcrypt_enc_get_key_size($module)); $ivSize=mcrypt_enc_get_iv_size($module); $iv=$this->substr($data,0,$ivSize); mcrypt_generic_init($module,$key,$iv); @@ -271,12 +295,15 @@ public function hashData($data,$key=null) */ public function validateData($data,$key=null) { + if (!is_string($data)) + return false; + $len=$this->strlen($this->computeHMAC('test')); if($this->strlen($data)>=$len) { $hmac=$this->substr($data,0,$len); $data2=$this->substr($data,$len,$this->strlen($data)); - return $hmac===$this->computeHMAC($data2,$key)?$data2:false; + return $this->compareString($hmac,$this->computeHMAC($data2,$key))?$data2:false; } else return false; @@ -489,4 +516,97 @@ private function substr($string,$start,$length) { return $this->_mbstring ? mb_substr($string,$start,$length,'8bit') : substr($string,$start,$length); } + + /** + * Checks if a key is valid for {@link cryptAlgorithm}. + * @param string $key the key to check + * @return boolean the validation result + * @throws CException if the supported key lengths of the cipher are unknown + */ + protected function validateEncryptionKey($key) + { + if(is_string($key)) + { + $supportedKeyLengths=mcrypt_module_get_supported_key_sizes($this->cryptAlgorithm); + + if($supportedKeyLengths) + { + if(!in_array($this->strlen($key),$supportedKeyLengths)) { + throw new CException(Yii::t('yii','Encryption key length can be {keyLengths}',array('{keyLengths}'=>implode(',',$supportedKeyLengths).'.'))); + } + } + elseif(isset(self::$encryptionKeyMinimumLengths[$this->cryptAlgorithm])) + { + $minLength=self::$encryptionKeyMinimumLengths[$this->cryptAlgorithm]; + $maxLength=mcrypt_module_get_algo_key_size($this->cryptAlgorithm); + if($this->strlen($key)<$minLength || $this->strlen($key)>$maxLength) + throw new CException(Yii::t('yii','Encryption key length must be between {minLength} and {maxLength}.',array('{minLength}'=>$minLength,'{maxLength}'=>$maxLength))); + } + else + throw new CException(Yii::t('yii','Failed to validate key. Supported key lengths of cipher not known.')); + } + else + throw new CException(Yii::t('yii','Encryption key should be a string.')); + } + + /** + * Decrypts legacy ciphertext which was produced by the old, broken implementation of encrypt(). + * @deprecated use only to convert data encrypted prior to 1.1.16 + * @param string $data data to be decrypted. + * @param string $key the decryption key. This defaults to null, meaning the key should be loaded from persistent storage. + * @param string|array $cipher the algorithm to be used + * @return string the decrypted data + * @throws CException if PHP Mcrypt extension is not loaded + * @throws CException if the key is missing + */ + public function legacyDecrypt($data,$key=null,$cipher='des') + { + if (!$key) + { + $key=Yii::app()->getGlobalState(self::STATE_ENCRYPTION_KEY); + if(!$key) + throw new CException(Yii::t('yii','No encryption key specified.')); + } + + if(extension_loaded('mcrypt')) + { + if(is_array($cipher)) + $module=@call_user_func_array('mcrypt_module_open',$cipher); + else + $module=@mcrypt_module_open($cipher,'', MCRYPT_MODE_CBC,''); + + if($module===false) + throw new CException(Yii::t('yii','Failed to initialize the mcrypt module.')); + } + else + throw new CException(Yii::t('yii','CSecurityManager requires PHP mcrypt extension to be loaded in order to use data encryption feature.')); + + $derivedKey=$this->substr(md5($key),0,mcrypt_enc_get_key_size($module)); + $ivSize=mcrypt_enc_get_iv_size($module); + $iv=$this->substr($data,0,$ivSize); + mcrypt_generic_init($module,$derivedKey,$iv); + $decrypted=mdecrypt_generic($module,$this->substr($data,$ivSize,$this->strlen($data))); + mcrypt_generic_deinit($module); + mcrypt_module_close($module); + return rtrim($decrypted,"\0"); + } + + /** + * Performs string comparison using timing attack resistant approach. + * @see http://codereview.stackexchange.com/questions/13512 + * @param string $expected string to compare. + * @param string $actual user-supplied string. + * @return boolean whether strings are equal. + */ + public function compareString($expected,$actual) + { + $expected.="\0"; + $actual.="\0"; + $expectedLength=$this->strlen($expected); + $actualLength=$this->strlen($actual); + $diff=$expectedLength-$actualLength; + for($i=0;$i<$actualLength;$i++) + $diff|=(ord($actual[$i])^ord($expected[$i%$expectedLength])); + return $diff===0; + } } diff --git a/framework/base/interfaces.php b/framework/base/interfaces.php index d879d07ff5e..052992ce9ad 100644 --- a/framework/base/interfaces.php +++ b/framework/base/interfaces.php @@ -350,7 +350,7 @@ public function checkAccess($itemName,$userId,$params=array()); * Creates an authorization item. * An authorization item represents an action permission (e.g. creating a post). * It has three types: operation, task and role. - * Authorization items form a hierarchy. Higher level items inheirt permissions representing + * Authorization items form a hierarchy. Higher level items inherit permissions representing * by lower level items. * @param string $name the item name. This must be a unique identifier. * @param integer $type the item type (0: operation, 1: task, 2: role). diff --git a/framework/caching/CApcCache.php b/framework/caching/CApcCache.php index ffc3235ebf0..0fbbb1f20e8 100644 --- a/framework/caching/CApcCache.php +++ b/framework/caching/CApcCache.php @@ -103,6 +103,9 @@ protected function deleteValue($key) */ protected function flushValues() { + if(extension_loaded('apcu')) + return apc_clear_cache(); + return apc_clear_cache('user'); } } diff --git a/framework/caching/CFileCache.php b/framework/caching/CFileCache.php index f8ed7896632..330e74d4d9e 100644 --- a/framework/caching/CFileCache.php +++ b/framework/caching/CFileCache.php @@ -30,10 +30,24 @@ class CFileCache extends CCache * using 'protected/runtime/cache' as the directory. */ public $cachePath; + /** + * @var integer the permission to be set for directory to store cache files + * This value will be used by PHP chmod function. + * Defaults to 0777, meaning the directory can be read, written and executed by all users. + * @since 1.1.16 + */ + public $cachePathMode=0777; /** * @var string cache file suffix. Defaults to '.bin'. */ public $cacheFileSuffix='.bin'; + /** + * @var integer the permission to be set for new cache files. + * This value will be used by PHP chmod function. + * Defaults to 0666, meaning the file is read-writable by all users. + * @since 1.1.16 + */ + public $cacheFileMode=0666; /** * @var integer the level of sub-directories to store cache files. Defaults to 0, * meaning no sub-directories. If the system has huge number of cache files (e.g. 10K+), @@ -64,7 +78,10 @@ public function init() if($this->cachePath===null) $this->cachePath=Yii::app()->getRuntimePath().DIRECTORY_SEPARATOR.'cache'; if(!is_dir($this->cachePath)) - mkdir($this->cachePath,0777,true); + { + mkdir($this->cachePath,$this->cachePathMode,true); + chmod($this->cachePath,$this->cachePathMode); + } } /** @@ -142,10 +159,14 @@ protected function setValue($key,$value,$expire) $cacheFile=$this->getCacheFile($key); if($this->directoryLevel>0) - @mkdir(dirname($cacheFile),0777,true); + { + $cacheDir=dirname($cacheFile); + @mkdir($cacheDir,$this->cachePathMode,true); + @chmod($cacheDir,$this->cachePathMode); + } if(@file_put_contents($cacheFile,$this->embedExpiry ? $expire.$value : $value,LOCK_EX)!==false) { - @chmod($cacheFile,0777); + @chmod($cacheFile,$this->cacheFileMode); return $this->embedExpiry ? true : @touch($cacheFile,$expire); } else diff --git a/framework/caching/CRedisCache.php b/framework/caching/CRedisCache.php index 844d850bdfe..56b347a62a9 100644 --- a/framework/caching/CRedisCache.php +++ b/framework/caching/CRedisCache.php @@ -108,7 +108,7 @@ protected function connect() * * See {@link http://redis.io/topics/protocol redis protocol description} * for details on the mentioned reply types. - * @trows CException for commands that return {@link http://redis.io/topics/protocol#error-reply error reply}. + * @throws CException for commands that return {@link http://redis.io/topics/protocol#error-reply error reply}. */ public function executeCommand($name,$params=array()) { diff --git a/framework/cli/commands/MessageCommand.php b/framework/cli/commands/MessageCommand.php index d4d7be61db2..d1e0e2cfbf2 100644 --- a/framework/cli/commands/MessageCommand.php +++ b/framework/cli/commands/MessageCommand.php @@ -61,6 +61,9 @@ public function getHelp() instead of being enclosed between a pair of '@@' marks. - sort: sort messages by key when merging, regardless of their translation state (new, obsolete, translated.) + - fileHeader: A boolean indicating whether the file should contain a default + comment that explains the message file or a string representing + some PHP code or comment to add before the return tag in the message file. EOD; } @@ -98,6 +101,9 @@ public function run($args) if(!isset($sort)) $sort = false; + if(!isset($fileHeader)) + $fileHeader = true; + $options=array(); if(isset($fileTypes)) $options['fileTypes']=$fileTypes; @@ -117,7 +123,7 @@ public function run($args) foreach($messages as $category=>$msgs) { $msgs=array_values(array_unique($msgs)); - $this->generateMessageFile($msgs,$dir.DIRECTORY_SEPARATOR.$category.'.php',$overwrite,$removeOld,$sort); + $this->generateMessageFile($msgs,$dir.DIRECTORY_SEPARATOR.$category.'.php',$overwrite,$removeOld,$sort,$fileHeader); } } } @@ -147,7 +153,7 @@ protected function extractMessages($fileName,$translator) return $messages; } - protected function generateMessageFile($messages,$fileName,$overwrite,$removeOld,$sort) + protected function generateMessageFile($messages,$fileName,$overwrite,$removeOld,$sort,$fileHeader) { echo "Saving messages to $fileName..."; if(is_file($fileName)) @@ -201,8 +207,8 @@ protected function generateMessageFile($messages,$fileName,$overwrite,$removeOld echo "saved.\n"; } $array=str_replace("\r",'',var_export($merged,true)); - $content=<<usageError('Please specify which version, timestamp or datetime to migrate to.'); + + if((string)(int)$args[0]==$args[0]) + return $this->migrateToTime($args[0]); + elseif(($time=strtotime($args[0]))!==false) + return $this->migrateToTime($time); + else + return $this->migrateToVersion($args[0]); + } + + private function migrateToTime($time) + { + $data=$this->getDbConnection()->createCommand() + ->select('version,apply_time') + ->from($this->migrationTable) + ->where('apply_time<=:time',array(':time'=>$time)) + ->order('apply_time DESC') + ->limit(1) + ->queryRow(); + + if($data===false) + { + echo "Error: Unable to find a version before ".date('Y-m-d H:i:s',$time).".\n"; + return 1; + } else - $this->usageError('Please specify which version to migrate to.'); + { + echo "Found version ".$data['version']." applied at ".date('Y-m-d H:i:s',$data['apply_time']).", it is before ".date('Y-m-d H:i:s',$time).".\n"; + return $this->migrateToVersion(substr($data['version'],1,13)); + } + } + private function migrateToVersion($version) + { $originalVersion=$version; if(preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/',$version,$matches)) $version='m'.$matches[1]; @@ -466,7 +496,7 @@ protected function createMigrationHistoryTable() $db=$this->getDbConnection(); echo 'Creating migration history table "'.$this->migrationTable.'"...'; $db->createCommand()->createTable($this->migrationTable,array( - 'version'=>'string NOT NULL PRIMARY KEY', + 'version'=>'varchar(180) NOT NULL PRIMARY KEY', 'apply_time'=>'integer', )); $db->createCommand()->insert($this->migrationTable,array( @@ -530,6 +560,16 @@ public function getHelp() * yiic migrate to 101129_185401 Migrates up or down to version 101129_185401. + * yiic migrate to 1392447720 + Migrates to the given UNIX timestamp. This means that all the versions + applied after the specified timestamp will be reverted. Versions applied + before won't be touched. + + * yiic migrate to "2014-02-15 13:00:50" + Migrates to the given datetime parseable by the strtotime() function. + This means that all the versions applied after the specified datetime + will be reverted. Versions applied before won't be touched. + * yiic migrate mark 101129_185401 Modifies the migration history up or down to version 101129_185401. No actual migration will be performed. diff --git a/framework/cli/commands/ShellCommand.php b/framework/cli/commands/ShellCommand.php index 3aed3f10575..5855f261afa 100644 --- a/framework/cli/commands/ShellCommand.php +++ b/framework/cli/commands/ShellCommand.php @@ -104,11 +104,8 @@ protected function runShell() // disable E_NOTICE so that the shell is more friendly error_reporting(E_ALL ^ E_NOTICE); - $_runner_=new CConsoleCommandRunner; - $_runner_->addCommands(dirname(__FILE__).'/shell'); - $_runner_->addCommands(Yii::getPathOfAlias('application.commands.shell')); - if(($_path_=@getenv('YIIC_SHELL_COMMAND_PATH'))!==false) - $_runner_->addCommands($_path_); + $_runner_=$this->createCommandRunner(); + $this->addCommands($_runner_); $_commands_=$_runner_->commands; $log=Yii::app()->log; @@ -139,6 +136,29 @@ protected function runShell() } } } + + /** + * Creates a commands runner + * @return CConsoleCommandRunner + * @since 1.1.16 + */ + protected function createCommandRunner() + { + return new CConsoleCommandRunner; + } + + /** + * Adds commands to runner + * @param CConsoleCommandRunner $runner + * @since 1.1.16 + */ + protected function addCommands(CConsoleCommandRunner $runner) + { + $runner->addCommands(Yii::getPathOfAlias('system.cli.commands.shell')); + $runner->addCommands(Yii::getPathOfAlias('application.commands.shell')); + if(($_path_=@getenv('YIIC_SHELL_COMMAND_PATH'))!==false) + $runner->addCommands($_path_); + } } class ShellException extends CException diff --git a/framework/cli/views/shell/crud/view.php b/framework/cli/views/shell/crud/view.php index 692b328a739..fc32d935c58 100644 --- a/framework/cli/views/shell/crud/view.php +++ b/framework/cli/views/shell/crud/view.php @@ -21,7 +21,7 @@ array('label'=>'List ', 'url'=>array('index')), array('label'=>'Create ', 'url'=>array('create')), array('label'=>'Update ', 'url'=>array('update', 'id'=>$model->)), - array('label'=>'Delete ', 'url'=>'#', 'linkOptions'=>array('submit'=>array('delete','id'=>$model->),'confirm'=>'Are you sure you want to delete this item?')), + array('label'=>'Delete ', 'url'=>'#', 'linkOptions'=>array('submit'=>array('delete','id'=>$model->),'confirm'=>Yii::t('zii','Are you sure you want to delete this item?'))), array('label'=>'Manage ', 'url'=>array('admin')), ); ?> diff --git a/framework/cli/views/webapp/protected/config/console.php b/framework/cli/views/webapp/protected/config/console.php index 346a9764803..dd3b3bc5b63 100644 --- a/framework/cli/views/webapp/protected/config/console.php +++ b/framework/cli/views/webapp/protected/config/console.php @@ -11,19 +11,10 @@ // application components 'components'=>array( - 'db'=>array( - 'connectionString' => 'sqlite:'.dirname(__FILE__).'/../data/testdrive.db', - ), - // uncomment the following to use a MySQL database - /* - 'db'=>array( - 'connectionString' => 'mysql:host=localhost;dbname=testdrive', - 'emulatePrepare' => true, - 'username' => 'root', - 'password' => '', - 'charset' => 'utf8', - ), - */ + + // database settings are configured in database.php + 'db'=>require(dirname(__FILE__).'/database.php'), + 'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( @@ -33,5 +24,6 @@ ), ), ), + ), -); \ No newline at end of file +); diff --git a/framework/cli/views/webapp/protected/config/main.php b/framework/cli/views/webapp/protected/config/main.php index 6919c74f6f9..052c5eca9ac 100644 --- a/framework/cli/views/webapp/protected/config/main.php +++ b/framework/cli/views/webapp/protected/config/main.php @@ -32,10 +32,12 @@ // application components 'components'=>array( + 'user'=>array( // enable cookie-based authentication 'allowAutoLogin'=>true, ), + // uncomment the following to enable URLs in path-format /* 'urlManager'=>array( @@ -47,23 +49,15 @@ ), ), */ - 'db'=>array( - 'connectionString' => 'sqlite:'.dirname(__FILE__).'/../data/testdrive.db', - ), - // uncomment the following to use a MySQL database - /* - 'db'=>array( - 'connectionString' => 'mysql:host=localhost;dbname=testdrive', - 'emulatePrepare' => true, - 'username' => 'root', - 'password' => '', - 'charset' => 'utf8', - ), - */ + + // database settings are configured in database.php + 'db'=>require(dirname(__FILE__).'/database.php'), + 'errorHandler'=>array( // use 'site/error' action to display errors 'errorAction'=>'site/error', ), + 'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( @@ -79,6 +73,7 @@ */ ), ), + ), // application-level parameters that can be accessed @@ -87,4 +82,4 @@ // this is used in contact page 'adminEmail'=>'webmaster@example.com', ), -); \ No newline at end of file +); diff --git a/framework/cli/views/webapp/protected/views/layouts/main.php b/framework/cli/views/webapp/protected/views/layouts/main.php index daf905eab23..a8533aea937 100644 --- a/framework/cli/views/webapp/protected/views/layouts/main.php +++ b/framework/cli/views/webapp/protected/views/layouts/main.php @@ -1,19 +1,19 @@ - - + + - - + + - - + + - - + + <?php echo CHtml::encode($this->pageTitle); ?> diff --git a/framework/cli/views/webapp/protected/yiic b/framework/cli/views/webapp/protected/yiic old mode 100644 new mode 100755 diff --git a/framework/console/CConsoleApplication.php b/framework/console/CConsoleApplication.php index 078696a6020..04700ee029a 100644 --- a/framework/console/CConsoleApplication.php +++ b/framework/console/CConsoleApplication.php @@ -75,7 +75,7 @@ class CConsoleApplication extends CApplication protected function init() { parent::init(); - if(!isset($_SERVER['argv'])) // || strncasecmp(php_sapi_name(),'cli',3)) + if(empty($_SERVER['argv'])) die('This script must be run from the command line.'); $this->_runner=$this->createCommandRunner(); $this->_runner->commands=$this->commandMap; diff --git a/framework/console/CConsoleCommand.php b/framework/console/CConsoleCommand.php index 34e29452e6d..d29c4a0a980 100644 --- a/framework/console/CConsoleCommand.php +++ b/framework/console/CConsoleCommand.php @@ -362,8 +362,8 @@ public function copyFiles($fileList) $overwriteAll=false; foreach($fileList as $name=>$file) { - $source=strtr($file['source'],'/\\',DIRECTORY_SEPARATOR); - $target=strtr($file['target'],'/\\',DIRECTORY_SEPARATOR); + $source=strtr($file['source'],'/\\',DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR); + $target=strtr($file['target'],'/\\',DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR); $callback=isset($file['callback']) ? $file['callback'] : null; $params=isset($file['params']) ? $file['params'] : null; diff --git a/framework/db/CDbCommand.php b/framework/db/CDbCommand.php index 04d2c19cd50..3bd1d8587ad 100644 --- a/framework/db/CDbCommand.php +++ b/framework/db/CDbCommand.php @@ -122,7 +122,7 @@ public function __sleep() /** * Set the default fetch mode for this statement * @param mixed $mode fetch mode - * @return CDbCommand + * @return static * @see http://www.php.net/manual/en/function.PDOStatement-setFetchMode.php * @since 1.1.7 */ @@ -138,7 +138,7 @@ public function setFetchMode($mode) * This method is mainly used when a command object is being reused * multiple times for building different queries. * Calling this method will clean up all internal states of the command object. - * @return CDbCommand this command instance + * @return static this command instance * @since 1.1.6 */ public function reset() @@ -165,7 +165,7 @@ public function getText() * Specifies the SQL statement to be executed. * Any previous execution will be terminated or cancel. * @param string $value the SQL statement to be executed - * @return CDbCommand this command instance + * @return static this command instance */ public function setText($value) { @@ -239,7 +239,7 @@ public function cancel() * @param integer $dataType SQL data type of the parameter. If null, the type is determined by the PHP type of the value. * @param integer $length length of the data type * @param mixed $driverOptions the driver-specific options (this is available since version 1.1.6) - * @return CDbCommand the current command being executed + * @return static the current command being executed * @see http://www.php.net/manual/en/function.PDOStatement-bindParam.php */ public function bindParam($name, &$value, $dataType=null, $length=null, $driverOptions=null) @@ -265,7 +265,7 @@ public function bindParam($name, &$value, $dataType=null, $length=null, $driverO * placeholders, this will be the 1-indexed position of the parameter. * @param mixed $value The value to bind to the parameter * @param integer $dataType SQL data type of the parameter. If null, the type is determined by the PHP type of the value. - * @return CDbCommand the current command being executed + * @return static the current command being executed * @see http://www.php.net/manual/en/function.PDOStatement-bindValue.php */ public function bindValue($name, $value, $dataType=null) @@ -286,7 +286,7 @@ public function bindValue($name, $value, $dataType=null) * @param array $values the values to be bound. This must be given in terms of an associative * array with array keys being the parameter names, and array values the corresponding parameter values. * For example, array(':name'=>'John', ':age'=>25). - * @return CDbCommand the current command being executed + * @return static the current command being executed * @since 1.1.5 */ public function bindValues($values) @@ -489,7 +489,7 @@ private function queryInternal($method,$mode,$params=array()) && ($cache=Yii::app()->getComponent($this->_connection->queryCacheID))!==null) { $this->_connection->queryCachingCount--; - $cacheKey='yii:dbquery'.$this->_connection->connectionString.':'.$this->_connection->username; + $cacheKey='yii:dbquery'.':'.$method.':'.$this->_connection->connectionString.':'.$this->_connection->username; $cacheKey.=':'.$this->getText().':'.serialize(array_merge($this->_paramLog,$params)); if(($result=$cache->get($cacheKey))!==false) { @@ -562,8 +562,6 @@ public function buildQuery($query) if(!empty($query['from'])) $sql.="\nFROM ".$query['from']; - else - throw new CDbException(Yii::t('yii','The DB query must contain the "from" portion.')); if(!empty($query['join'])) $sql.="\n".(is_array($query['join']) ? implode("\n",$query['join']) : $query['join']); @@ -600,7 +598,7 @@ public function buildQuery($query) * (which means the column contains a DB expression). * @param string $option additional option that should be appended to the 'SELECT' keyword. For example, * in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used. This parameter is supported since version 1.1.8. - * @return CDbCommand the command object itself + * @return static the command object itself * @since 1.1.6 */ public function select($columns='*', $option='') @@ -692,7 +690,7 @@ public function setDistinct($value) * Table names can contain schema prefixes (e.g. 'public.tbl_user') and/or table aliases (e.g. 'tbl_user u'). * The method will automatically quote the table names unless it contains some parenthesis * (which means the table is given as a sub-query or DB expression). - * @return CDbCommand the command object itself + * @return static the command object itself * @since 1.1.6 */ public function from($tables) @@ -774,7 +772,7 @@ public function setFrom($value) * * @param mixed $conditions the conditions that should be put in the WHERE part. * @param array $params the parameters (name=>value) to be bound to the query - * @return CDbCommand the command object itself + * @return static the command object itself * @since 1.1.6 */ public function where($conditions, $params=array()) @@ -795,7 +793,7 @@ public function where($conditions, $params=array()) * * @param mixed $conditions the conditions that should be appended to the WHERE part. * @param array $params the parameters (name=>value) to be bound to the query. - * @return CDbCommand the command object itself. + * @return static the command object itself. * @since 1.1.13 */ public function andWhere($conditions,$params=array()) @@ -819,7 +817,7 @@ public function andWhere($conditions,$params=array()) * * @param mixed $conditions the conditions that should be appended to the WHERE part. * @param array $params the parameters (name=>value) to be bound to the query. - * @return CDbCommand the command object itself. + * @return static the command object itself. * @since 1.1.13 */ public function orWhere($conditions,$params=array()) @@ -875,7 +873,7 @@ public function join($table, $conditions, $params=array()) /** * Returns the join part in the query. * @return mixed the join part in the query. This can be an array representing - * multiple join fragments, or a string representing a single jojin fragment. + * multiple join fragments, or a string representing a single join fragment. * Each join fragment will contain the proper join operator (e.g. LEFT JOIN). * @since 1.1.6 */ @@ -960,13 +958,43 @@ public function naturalJoin($table) return $this->joinInternal('natural join', $table); } + /** + * Appends a NATURAL LEFT JOIN part to the query. + * Note that not all DBMS support NATURAL LEFT JOIN. + * @param string $table the table to be joined. + * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). + * The method will automatically quote the table name unless it contains some parenthesis + * (which means the table is given as a sub-query or DB expression). + * @return CDbCommand the command object itself + * @since 1.1.16 + */ + public function naturalLeftJoin($table) + { + return $this->joinInternal('natural left join', $table); + } + + /** + * Appends a NATURAL RIGHT JOIN part to the query. + * Note that not all DBMS support NATURAL RIGHT JOIN. + * @param string $table the table to be joined. + * Table name can contain schema prefix (e.g. 'public.tbl_user') and/or table alias (e.g. 'tbl_user u'). + * The method will automatically quote the table name unless it contains some parenthesis + * (which means the table is given as a sub-query or DB expression). + * @return CDbCommand the command object itself + * @since 1.1.16 + */ + public function naturalRightJoin($table) + { + return $this->joinInternal('natural right join', $table); + } + /** * Sets the GROUP BY part of the query. * @param mixed $columns the columns to be grouped by. * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')). * The method will automatically quote the column names unless a column contains some parenthesis * (which means the column contains a DB expression). - * @return CDbCommand the command object itself + * @return static the command object itself * @since 1.1.6 */ public function group($columns) @@ -1015,7 +1043,7 @@ public function setGroup($value) * @param mixed $conditions the conditions to be put after HAVING. * Please refer to {@link where} on how to specify conditions. * @param array $params the parameters (name=>value) to be bound to the query - * @return CDbCommand the command object itself + * @return static the command object itself * @since 1.1.6 */ public function having($conditions, $params=array()) @@ -1060,7 +1088,7 @@ public function setHaving($value) * $criteria->order('(1)'); *
    * - * @return CDbCommand the command object itself + * @return static the command object itself * @since 1.1.6 */ public function order($columns) @@ -1113,7 +1141,7 @@ public function setOrder($value) * Sets the LIMIT part of the query. * @param integer $limit the limit * @param integer $offset the offset - * @return CDbCommand the command object itself + * @return static the command object itself * @since 1.1.6 */ public function limit($limit, $offset=null) @@ -1148,7 +1176,7 @@ public function setLimit($value) /** * Sets the OFFSET part of the query. * @param integer $offset the offset - * @return CDbCommand the command object itself + * @return static the command object itself * @since 1.1.6 */ public function offset($offset) @@ -1181,7 +1209,7 @@ public function setOffset($value) /** * Appends a SQL statement using UNION operator. * @param string $sql the SQL statement to be appended using UNION - * @return CDbCommand the command object itself + * @return static the command object itself * @since 1.1.6 */ public function union($sql) @@ -1422,9 +1450,9 @@ public function alterColumn($table, $column, $type) * The method will properly quote the table and column names. * @param string $name the name of the foreign key constraint. * @param string $table the table that the foreign key constraint will be added to. - * @param string $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas. + * @param string|array $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas or pass as an array of column names. * @param string $refTable the table that the foreign key references to. - * @param string $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas. + * @param string|array $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas or pass as an array of column names. * @param string $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL * @param string $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL * @return integer number of rows affected by the execution. @@ -1451,15 +1479,15 @@ public function dropForeignKey($name, $table) * Builds and executes a SQL statement for creating a new index. * @param string $name the name of the index. The name will be properly quoted by the method. * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method. - * @param string $column the column(s) that should be included in the index. If there are multiple columns, please separate them - * by commas. The column names will be properly quoted by the method. + * @param string|array $columns the column(s) that should be included in the index. If there are multiple columns, please separate them + * by commas or pass as an array of column names. Each column name will be properly quoted by the method, unless a parenthesis is found in the name. * @param boolean $unique whether to add UNIQUE constraint on the created index. * @return integer number of rows affected by the execution. * @since 1.1.6 */ - public function createIndex($name, $table, $column, $unique=false) + public function createIndex($name, $table, $columns, $unique=false) { - return $this->setText($this->getConnection()->getSchema()->createIndex($name, $table, $column, $unique))->execute(); + return $this->setText($this->getConnection()->getSchema()->createIndex($name, $table, $columns, $unique))->execute(); } /** @@ -1556,7 +1584,7 @@ private function processConditions($conditions) * @param mixed $conditions the join condition that should appear in the ON part. * Please refer to {@link where} on how to specify conditions. * @param array $params the parameters (name=>value) to be bound to the query - * @return CDbCommand the command object itself + * @return static the command object itself * @since 1.1.6 */ private function joinInternal($type, $table, $conditions='', $params=array()) @@ -1587,7 +1615,8 @@ private function joinInternal($type, $table, $conditions='', $params=array()) * Builds a SQL statement for creating a primary key constraint. * @param string $name the name of the primary key constraint to be created. The name will be properly quoted by the method. * @param string $table the table who will be inheriting the primary key. The name will be properly quoted by the method. - * @param string $columns the column/s where the primary key will be effected. The name will be properly quoted by the method. + * @param string|array $columns comma separated string or array of columns that the primary key will consist of. + * Array value can be passed since 1.1.14. * @return integer number of rows affected by the execution. * @since 1.1.13 */ diff --git a/framework/db/CDbConnection.php b/framework/db/CDbConnection.php index 4390ca75aa2..3fd88d83388 100644 --- a/framework/db/CDbConnection.php +++ b/framework/db/CDbConnection.php @@ -78,6 +78,20 @@ * ) * * + * Use the {@link driverName} property if you want to force the DB connection to use a particular driver + * by the given name, disregarding of what was set in the {@link connectionString} property. This might + * be useful when working with ODBC connections. Sample code: + * + *
    + * 'db'=>array(
    + *     'class'=>'CDbConnection',
    + *     'driverName'=>'mysql',
    + *     'connectionString'=>'odbc:Driver={MySQL};Server=127.0.0.1;Database=test',
    + *     'username'=>'',
    + *     'password'=>'',
    + * ),
    + * 
    + * * @property boolean $active Whether the DB connection is established. * @property PDO $pdoInstance The PDO instance, null if the connection is not established yet. * @property CDbTransaction $currentTransaction The currently active transaction. Null if no active transaction. @@ -88,7 +102,8 @@ * @property mixed $nullConversion How the null and empty strings are converted. * @property boolean $autoCommit Whether creating or updating a DB record will be automatically committed. * @property boolean $persistent Whether the connection is persistent or not. - * @property string $driverName Name of the DB driver. + * @property string $driverName Name of the DB driver. This property is read-write since 1.1.16. + * Before 1.1.15 it was read-only. * @property string $clientVersion The version information of the DB driver. * @property string $connectionStatus The status of the connection. * @property boolean $prefetch Whether the connection performs data prefetching. @@ -185,7 +200,7 @@ class CDbConnection extends CApplicationComponent public $autoConnect=true; /** * @var string the charset used for database connection. The property is only used - * for MySQL and PostgreSQL databases. Defaults to null, meaning using default charset + * for MySQL, MariaDB and PostgreSQL databases. Defaults to null, meaning using default charset * as specified by the database. * * Note that if you're using GBK or BIG5 then it's highly recommended to @@ -233,9 +248,10 @@ class CDbConnection extends CApplicationComponent * @since 1.1.6 */ public $driverMap=array( + 'cubrid'=>'CCubridSchema', // CUBRID 'pgsql'=>'CPgsqlSchema', // PostgreSQL 'mysqli'=>'CMysqlSchema', // MySQL - 'mysql'=>'CMysqlSchema', // MySQL + 'mysql'=>'CMysqlSchema', // MySQL,MariaDB 'sqlite'=>'CSqliteSchema', // sqlite 3 'sqlite2'=>'CSqliteSchema', // sqlite 2 'mssql'=>'CMssqlSchema', // Mssql driver on windows hosts @@ -250,6 +266,7 @@ class CDbConnection extends CApplicationComponent */ public $pdoClass = 'PDO'; + private $_driverName; private $_attributes=array(); private $_active=false; private $_pdo; @@ -347,7 +364,7 @@ public function setActive($value) * the query results into cache. * @param integer $queryCount number of SQL queries that need to be cached after calling this method. Defaults to 1, * meaning that the next SQL query will be cached. - * @return CDbConnection the connection instance itself. + * @return static the connection instance itself. * @since 1.1.7 */ public function cache($duration, $dependency=null, $queryCount=1) @@ -413,9 +430,8 @@ protected function close() protected function createPdoInstance() { $pdoClass=$this->pdoClass; - if(($pos=strpos($this->connectionString,':'))!==false) + if(($driver=$this->getDriverName())!==null) { - $driver=strtolower(substr($this->connectionString,0,$pos)); if($driver==='mssql' || $driver==='dblib') $pdoClass='CMssqlPdoAdapter'; elseif($driver==='sqlsrv') @@ -437,7 +453,7 @@ protected function createPdoInstance() /** * Initializes the open db connection. * This method is invoked right after the db connection is established. - * The default implementation is to set the charset for MySQL and PostgreSQL database connections. + * The default implementation is to set the charset for MySQL, MariaDB and PostgreSQL database connections. * @param PDO $pdo the PDO instance */ protected function initConnection($pdo) @@ -687,14 +703,29 @@ public function setPersistent($value) } /** - * Returns the name of the DB driver - * @return string name of the DB driver + * Returns the name of the DB driver. + * @return string name of the DB driver. */ public function getDriverName() { - if(($pos=strpos($this->connectionString, ':'))!==false) - return strtolower(substr($this->connectionString, 0, $pos)); - // return $this->getAttribute(PDO::ATTR_DRIVER_NAME); + if($this->_driverName!==null) + return $this->_driverName; + elseif(($pos=strpos($this->connectionString,':'))!==false) + return $this->_driverName=strtolower(substr($this->connectionString,0,$pos)); + //return $this->getAttribute(PDO::ATTR_DRIVER_NAME); + } + + /** + * Changes the name of the DB driver. Overrides value extracted from the {@link connectionString}, + * which is behavior by default. + * @param string $driverName to be set. Valid values are the keys from the {@link driverMap} property. + * @see getDriverName + * @see driverName + * @since 1.1.16 + */ + public function setDriverName($driverName) + { + $this->_driverName=strtolower($driverName); } /** diff --git a/framework/db/CDbMigration.php b/framework/db/CDbMigration.php index 9849060b8be..05d607718a4 100644 --- a/framework/db/CDbMigration.php +++ b/framework/db/CDbMigration.php @@ -179,6 +179,23 @@ public function insert($table, $columns) echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n"; } + /** + * Creates and executes an INSERT SQL statement with multiple data. + * The method will properly escape the column names, and bind the values to be inserted. + * @param string $table the table that new rows will be inserted into. + * @param array $data an array of various column data (name=>value) to be inserted into the table. + * @since 1.1.16 + */ + public function insertMultiple($table, $data) + { + echo " > insert into $table ..."; + $time=microtime(true); + $builder=$this->getDbConnection()->getSchema()->getCommandBuilder(); + $command=$builder->createMultipleInsertCommand($table,$data); + $command->execute(); + echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n"; + } + /** * Creates and executes an UPDATE SQL statement. * The method will properly escape the column names and bind the values to be updated. @@ -335,15 +352,16 @@ public function alterColumn($table, $column, $type) * The method will properly quote the table and column names. * @param string $name the name of the foreign key constraint. * @param string $table the table that the foreign key constraint will be added to. - * @param string $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas. + * @param string|array $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas or pass as an array of column names. * @param string $refTable the table that the foreign key references to. - * @param string $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas. + * @param string|array $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas or pass as an array of column names. * @param string $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL * @param string $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL */ public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete=null, $update=null) { - echo " > add foreign key $name: $table ($columns) references $refTable ($refColumns) ..."; + echo " > add foreign key $name: $table (".(is_array($columns) ? implode(',', $columns) : $columns). + ") references $refTable (".(is_array($refColumns) ? implode(',', $refColumns) : $refColumns).") ..."; $time=microtime(true); $this->getDbConnection()->createCommand()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update); echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n"; @@ -366,15 +384,15 @@ public function dropForeignKey($name, $table) * Builds and executes a SQL statement for creating a new index. * @param string $name the name of the index. The name will be properly quoted by the method. * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method. - * @param string $column the column(s) that should be included in the index. If there are multiple columns, please separate them - * by commas. The column names will be properly quoted by the method. + * @param string|array $columns the column(s) that should be included in the index. If there are multiple columns, please separate them + * by commas or pass as an array of column names. Each column name will be properly quoted by the method, unless a parenthesis is found in the name. * @param boolean $unique whether to add UNIQUE constraint on the created index. */ - public function createIndex($name, $table, $column, $unique=false) + public function createIndex($name, $table, $columns, $unique=false) { - echo " > create".($unique ? ' unique':'')." index $name on $table ($column) ..."; + echo " > create".($unique ? ' unique':'')." index $name on $table (".(is_array($columns) ? implode(',', $columns) : $columns).") ..."; $time=microtime(true); - $this->getDbConnection()->createCommand()->createIndex($name, $table, $column, $unique); + $this->getDbConnection()->createCommand()->createIndex($name, $table, $columns, $unique); echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n"; } @@ -408,12 +426,13 @@ public function refreshTableSchema($table) * Builds and executes a SQL statement for creating a primary key, supports composite primary keys. * @param string $name name of the primary key constraint to add * @param string $table name of the table to add primary key to - * @param string $columns name of the column to utilise as primary key. If there are multiple columns, separate them with commas. + * @param string|array $columns comma separated string or array of columns that the primary key will consist of. + * Array value can be passed since 1.1.14. * @since 1.1.13 */ public function addPrimaryKey($name,$table,$columns) { - echo " > alter table $table add constraint $name primary key ($columns) ..."; + echo " > alter table $table add constraint $name primary key (".(is_array($columns) ? implode(',', $columns) : $columns).") ..."; $time=microtime(true); $this->getDbConnection()->createCommand()->addPrimaryKey($name,$table,$columns); echo " done (time: ".sprintf('%.3f', microtime(true)-$time)."s)\n"; diff --git a/framework/db/ar/CActiveFinder.php b/framework/db/ar/CActiveFinder.php index a93fd3b2694..58a39369dde 100644 --- a/framework/db/ar/CActiveFinder.php +++ b/framework/db/ar/CActiveFinder.php @@ -246,6 +246,9 @@ private function buildJoinTree($parent,$with,$options=null) if(!empty($options['scopes'])) $scopes=array_merge($scopes,(array)$options['scopes']); // no need for complex merging + if(!empty($options['joinOptions'])) + $relation->joinOptions=$options['joinOptions']; + $model->resetScope(false); $criteria=$model->getDbCriteria(); $criteria->scopes=$scopes; @@ -482,7 +485,6 @@ public function lazyFind($baseRecord) $query=new CJoinQuery($child); $query->selects=array($child->getColumnSelect($child->relation->select)); $query->conditions=array( - $child->relation->condition, $child->relation->on, ); $query->groups[]=$child->relation->group; @@ -537,6 +539,9 @@ private function applyLazyCondition($query,$record) $parent=$this->_parent; if($this->relation instanceof CManyManyRelation) { + $query->conditions=array( + $this->relation->condition, + ); $joinTableName=$this->relation->getJunctionTableName(); if(($joinTable=$schema->getTable($joinTableName))===null) throw new CDbException(Yii::t('yii','The relation "{relation}" in active record class "{class}" is not specified correctly: the join table "{joinTable}" given in the foreign key cannot be found in the database.', @@ -742,7 +747,7 @@ public function count($criteria=null) else { $select=is_array($criteria->select) ? implode(',',$criteria->select) : $criteria->select; - if($select!=='*' && !strncasecmp($select,'count',5)) + if($select!=='*' && preg_match('/^count\s*\(/',trim($select))) $query->selects=array($select); elseif(is_string($this->_table->primaryKey)) { @@ -965,9 +970,9 @@ public function getColumnSelect($select='*') $columns[]=$prefix.$schema->quoteColumnName($this->_table->primaryKey).' AS '.$schema->quoteColumnName($this->_pkAlias); elseif(is_array($this->_pkAlias)) { - foreach($this->_table->primaryKey as $name) - if(!isset($selected[$name])) - $columns[]=$prefix.$schema->quoteColumnName($name).' AS '.$schema->quoteColumnName($this->_pkAlias[$name]); + foreach($this->_pkAlias as $name=>$alias) + if(!isset($selected[$alias])) + $columns[]=$prefix.$schema->quoteColumnName($name).' AS '.$schema->quoteColumnName($alias); } } @@ -1113,7 +1118,12 @@ private function joinOneMany($fke,$fks,$pke,$parent) } if(!empty($this->relation->on)) $joins[]=$this->relation->on; - return $this->relation->joinType . ' ' . $this->getTableNameWithAlias() . ' ON (' . implode(') AND (',$joins).')'; + + if(!empty($this->relation->joinOptions) && is_string($this->relation->joinOptions)) + return $this->relation->joinType.' '.$this->getTableNameWithAlias().' '.$this->relation->joinOptions. + ' ON ('.implode(') AND (',$joins).')'; + else + return $this->relation->joinType.' '.$this->getTableNameWithAlias().' ON ('.implode(') AND (',$joins).')'; } /** @@ -1181,8 +1191,20 @@ private function joinManyMany($joinTable,$fks,$parent) if($parentCondition!==array() && $childCondition!==array()) { $join=$this->relation->joinType.' '.$joinTable->rawName.' '.$joinAlias; + + if(is_array($this->relation->joinOptions) && isset($this->relation->joinOptions[0]) && + is_string($this->relation->joinOptions[0])) + $join.=' '.$this->relation->joinOptions[0]; + elseif(!empty($this->relation->joinOptions) && is_string($this->relation->joinOptions)) + $join.=' '.$this->relation->joinOptions; + $join.=' ON ('.implode(') AND (',$parentCondition).')'; $join.=' '.$this->relation->joinType.' '.$this->getTableNameWithAlias(); + + if(is_array($this->relation->joinOptions) && isset($this->relation->joinOptions[1]) && + is_string($this->relation->joinOptions[1])) + $join.=' '.$this->relation->joinOptions[1]; + $join.=' ON ('.implode(') AND (',$childCondition).')'; if(!empty($this->relation->on)) $join.=' AND ('.$this->relation->on.')'; @@ -1315,7 +1337,7 @@ public function join($element) public function createCommand($builder) { $sql=($this->distinct ? 'SELECT DISTINCT ':'SELECT ') . implode(', ',$this->selects); - $sql.=' FROM ' . implode(' ',$this->joins); + $sql.=' FROM ' . implode(' ',array_unique($this->joins)); $conditions=array(); foreach($this->conditions as $condition) @@ -1501,9 +1523,10 @@ private function queryOneMany() $record->addRelatedRecord($relation->name,isset($stats[$pk])?$stats[$pk]:$relation->defaultValue,false); } - /* + /** * @param string $joinTableName jointablename * @param string $keys keys + * @throws CDbException */ private function queryManyMany($joinTableName,$keys) { diff --git a/framework/db/ar/CActiveRecord.php b/framework/db/ar/CActiveRecord.php index c40c5b80d75..fe9f125da0e 100644 --- a/framework/db/ar/CActiveRecord.php +++ b/framework/db/ar/CActiveRecord.php @@ -105,7 +105,7 @@ public function init() * the query results into cache. * @param integer $queryCount number of SQL queries that need to be cached after calling this method. Defaults to 1, * meaning that the next SQL query will be cached. - * @return CActiveRecord the active record instance itself. + * @return static the active record instance itself. * @since 1.1.7 */ public function cache($duration, $dependency=null, $queryCount=1) @@ -357,7 +357,7 @@ public function defaultScope() * Resets all scopes and criterias applied. * * @param boolean $resetDefault including default scope. This parameter available since 1.1.12 - * @return CActiveRecord + * @return static the AR instance itself * @since 1.1.2 */ public function resetScope($resetDefault=true) @@ -384,7 +384,7 @@ public function resetScope($resetDefault=true) * * * @param string $className active record class name. - * @return CActiveRecord active record model instance. + * @return static active record model instance. */ public static function model($className=__CLASS__) { @@ -435,7 +435,10 @@ public function refreshMetaData() */ public function tableName() { - return get_class($this); + $tableName = get_class($this); + if(($pos=strrpos($tableName,'\\')) !== false) + return substr($tableName,$pos+1); + return $tableName; } /** @@ -1072,7 +1075,7 @@ public function insert($attributes=null) { Yii::trace(get_class($this).'.insert()','system.db.ar.CActiveRecord'); $builder=$this->getCommandBuilder(); - $table=$this->getMetaData()->tableSchema; + $table=$this->getTableSchema(); $command=$builder->createInsertCommand($table,$this->getAttributes($attributes)); if($command->execute()) { @@ -1146,7 +1149,9 @@ public function update($attributes=null) * or an attribute value indexed by its name. If the latter, the record's * attribute will be changed accordingly before saving. * @throws CDbException if the record is new - * @return boolean whether the update is successful + * @return boolean whether the update is successful. Note that false is also returned if the saving + * was successfull but no attributes had changed and the database driver returns 0 for the number + * of updated records. */ public function saveAttributes($attributes) { @@ -1272,7 +1277,7 @@ public function equals($record) */ public function getPrimaryKey() { - $table=$this->getMetaData()->tableSchema; + $table=$this->getTableSchema(); if(is_string($table->primaryKey)) return $this->{$table->primaryKey}; elseif(is_array($table->primaryKey)) @@ -1296,7 +1301,7 @@ public function getPrimaryKey() public function setPrimaryKey($value) { $this->_pk=$this->getPrimaryKey(); - $table=$this->getMetaData()->tableSchema; + $table=$this->getTableSchema(); if(is_string($table->primaryKey)) $this->{$table->primaryKey}=$value; elseif(is_array($table->primaryKey)) @@ -1347,7 +1352,7 @@ protected function query($criteria,$all=false) { if(!$all) $criteria->limit=1; - $command=$this->getCommandBuilder()->createFindCommand($this->getTableSchema(),$criteria,$this->getTableAlias()); + $command=$this->getCommandBuilder()->createFindCommand($this->getTableSchema(),$criteria); return $all ? $this->populateRecords($command->queryAll(), true, $criteria->index) : $this->populateRecord($command->queryRow()); } else @@ -1447,7 +1452,7 @@ public function setTableAlias($alias) * @param array $params parameters to be bound to an SQL statement. * This is only used when the first parameter is a string (query condition). * In other cases, please use {@link CDbCriteria::params} to set parameters. - * @return CActiveRecord the record found. Null if no record is found. + * @return static the record found. Null if no record is found. */ public function find($condition='',$params=array()) { @@ -1461,7 +1466,7 @@ public function find($condition='',$params=array()) * See {@link find()} for detailed explanation about $condition and $params. * @param mixed $condition query condition or criteria. * @param array $params parameters to be bound to an SQL statement. - * @return CActiveRecord[] list of active records satisfying the specified condition. An empty array is returned if none is found. + * @return static[] list of active records satisfying the specified condition. An empty array is returned if none is found. */ public function findAll($condition='',$params=array()) { @@ -1476,7 +1481,7 @@ public function findAll($condition='',$params=array()) * @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). * @param mixed $condition query condition or criteria. * @param array $params parameters to be bound to an SQL statement. - * @return CActiveRecord the record found. Null if none is found. + * @return static the record found. Null if none is found. */ public function findByPk($pk,$condition='',$params=array()) { @@ -1492,7 +1497,7 @@ public function findByPk($pk,$condition='',$params=array()) * @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value). * @param mixed $condition query condition or criteria. * @param array $params parameters to be bound to an SQL statement. - * @return CActiveRecord[] the records found. An empty array is returned if none is found. + * @return static[] the records found. An empty array is returned if none is found. */ public function findAllByPk($pk,$condition='',$params=array()) { @@ -1509,7 +1514,7 @@ public function findAllByPk($pk,$condition='',$params=array()) * An attribute value can be an array which will be used to generate an IN condition. * @param mixed $condition query condition or criteria. * @param array $params parameters to be bound to an SQL statement. - * @return CActiveRecord the record found. Null if none is found. + * @return static the record found. Null if none is found. */ public function findByAttributes($attributes,$condition='',$params=array()) { @@ -1526,7 +1531,7 @@ public function findByAttributes($attributes,$condition='',$params=array()) * An attribute value can be an array which will be used to generate an IN condition. * @param mixed $condition query condition or criteria. * @param array $params parameters to be bound to an SQL statement. - * @return CActiveRecord[] the records found. An empty array is returned if none is found. + * @return static[] the records found. An empty array is returned if none is found. */ public function findAllByAttributes($attributes,$condition='',$params=array()) { @@ -1540,7 +1545,7 @@ public function findAllByAttributes($attributes,$condition='',$params=array()) * Finds a single active record with the specified SQL statement. * @param string $sql the SQL statement * @param array $params parameters to be bound to the SQL statement - * @return CActiveRecord the record found. Null if none is found. + * @return static the record found. Null if none is found. */ public function findBySql($sql,$params=array()) { @@ -1563,7 +1568,7 @@ public function findBySql($sql,$params=array()) * Finds all active records using the specified SQL statement. * @param string $sql the SQL statement * @param array $params parameters to be bound to the SQL statement - * @return CActiveRecord[] the records found. An empty array is returned if none is found. + * @return static[] the records found. An empty array is returned if none is found. */ public function findAllBySql($sql,$params=array()) { @@ -1592,8 +1597,8 @@ public function findAllBySql($sql,$params=array()) public function count($condition='',$params=array()) { Yii::trace(get_class($this).'.count()','system.db.ar.CActiveRecord'); - $builder=$this->getCommandBuilder(); $this->beforeCount(); + $builder=$this->getCommandBuilder(); $criteria=$builder->createCriteria($condition,$params); $this->applyScopes($criteria); @@ -1700,7 +1705,7 @@ public function exists($condition='',$params=array()) * ))->findAll(); * * - * @return CActiveRecord the AR object itself. + * @return static the AR object itself. */ public function with() { @@ -1719,7 +1724,7 @@ public function with() * Sets {@link CDbCriteria::together} property to be true. * This is only used in relational AR query. Please refer to {@link CDbCriteria::together} * for more details. - * @return CActiveRecord the AR object itself + * @return static the AR object itself * @since 1.1.4 */ public function together() @@ -1842,7 +1847,7 @@ public function deleteAllByAttributes($attributes,$condition='',$params=array()) * This method is internally used by the find methods. * @param array $attributes attribute values (column name=>column value) * @param boolean $callAfterFind whether to call {@link afterFind} after the record is populated. - * @return CActiveRecord the newly created active record. The class of the object is the same as the model class. + * @return static the newly created active record. The class of the object is the same as the model class. * Null is returned if the input data is false. */ public function populateRecord($attributes,$callAfterFind=true) @@ -1877,7 +1882,7 @@ public function populateRecord($attributes,$callAfterFind=true) * @param boolean $callAfterFind whether to call {@link afterFind} after each record is populated. * @param string $index the name of the attribute whose value will be used as indexes of the query result array. * If null, it means the array will be indexed by zero-based integers. - * @return CActiveRecord[] list of active records. + * @return static[] list of active records. */ public function populateRecords($data,$callAfterFind=true,$index=null) { @@ -1903,7 +1908,7 @@ public function populateRecords($data,$callAfterFind=true,$index=null) * For example, by creating a record based on the value of a column, * you may implement the so-called single-table inheritance mapping. * @param array $attributes list of attribute values for the active records. - * @return CActiveRecord the active record + * @return static the active record */ protected function instantiate($attributes) { @@ -1971,6 +1976,14 @@ class CBaseActiveRelation extends CComponent * @since 1.1.3 */ public $join=''; + /** + * @var string|array property for setting post-JOIN operations such as USE INDEX. + * String typed value can be used with JOINs for HAS_MANY and MANY_MANY relations, while array typed + * value designed to be used only with MANY_MANY relations. First array element will be used for junction + * table JOIN and second array element will be used for target table JOIN. + * @since 1.1.16 + */ + public $joinOptions=''; /** * @var string HAVING clause. For {@link CActiveRelation} descendant classes, column names * referenced in this property should be disambiguated with prefix 'relationName.'. @@ -2082,6 +2095,16 @@ class CStatRelation extends CBaseActiveRelation * receive a statistical query result. Defaults to 0. */ public $defaultValue=0; + /** + * @var mixed scopes to apply + * Can be set to the one of the following: + * + * @since 1.1.16 + */ + public $scopes; /** * Merges this relation with a criteria specified dynamically. @@ -2361,9 +2384,10 @@ public function __construct($model) if(($table=$model->getDbConnection()->getSchema()->getTable($tableName))===null) throw new CDbException(Yii::t('yii','The table "{table}" for active record class "{class}" cannot be found in the database.', array('{class}'=>$this->_modelClassName,'{table}'=>$tableName))); - if($table->primaryKey===null) + + if(($modelPk=$model->primaryKey())!==null || $table->primaryKey===null) { - $table->primaryKey=$model->primaryKey(); + $table->primaryKey=$modelPk; if(is_string($table->primaryKey) && isset($table->columns[$table->primaryKey])) $table->columns[$table->primaryKey]->isPrimaryKey=true; elseif(is_array($table->primaryKey)) diff --git a/framework/db/ar/CActiveRecordBehavior.php b/framework/db/ar/CActiveRecordBehavior.php index 373d6190323..443d15ba815 100644 --- a/framework/db/ar/CActiveRecordBehavior.php +++ b/framework/db/ar/CActiveRecordBehavior.php @@ -54,7 +54,7 @@ protected function beforeSave($event) * Responds to {@link CActiveRecord::onAfterSave} event. * Override this method and make it public if you want to handle the corresponding event * of the {@link CBehavior::owner owner}. - * @param CModelEvent $event event parameter + * @param CEvent $event event parameter */ protected function afterSave($event) { diff --git a/framework/db/schema/CDbCommandBuilder.php b/framework/db/schema/CDbCommandBuilder.php index 603b9129967..7f5afabf060 100644 --- a/framework/db/schema/CDbCommandBuilder.php +++ b/framework/db/schema/CDbCommandBuilder.php @@ -273,9 +273,12 @@ public function createMultipleInsertCommand($table,array $data) * If a key is not a valid column name, the corresponding value will be ignored. * @param array $templates templates for the SQL parts. * @return CDbCommand multiple insert command + * @throws CDbException if $data is empty. */ protected function composeMultipleInsertCommand($table,array $data,array $templates=array()) { + if (empty($data)) + throw new CDbException(Yii::t('yii','Can not generate multiple insert command with empty data set.')); $templates=array_merge( array( 'main'=>'INSERT INTO {{tableName}} ({{columnInsertNames}}) VALUES {{rowInsertValues}}', @@ -288,7 +291,7 @@ protected function composeMultipleInsertCommand($table,array $data,array $templa $templates ); $this->ensureTable($table); - $tableName=$this->getDbConnection()->quoteTableName($table->name); + $tableName=$table->rawName; $params=array(); $columnInsertNames=array(); $rowInsertValues=array(); @@ -499,7 +502,7 @@ public function applyOrder($sql,$orderBy) /** * Alters the SQL to apply LIMIT and OFFSET. - * Default implementation is applicable for PostgreSQL, MySQL and SQLite. + * Default implementation is applicable for PostgreSQL, MySQL, MariaDB and SQLite. * @param string $sql SQL query string without LIMIT and OFFSET. * @param integer $limit maximum number of rows, -1 to ignore limit. * @param integer $offset row offset, -1 to ignore offset. @@ -736,11 +739,11 @@ public function createSearchCondition($table,$columns,$keywords,$prefix=null,$ca $condition=array(); foreach($keywords as $keyword) { - $keyword='%'.strtr($keyword,array('%'=>'\%', '_'=>'\_')).'%'; + $keyword='%'.strtr($keyword,array('%'=>'\%', '_'=>'\_', '\\'=>'\\\\')).'%'; if($caseSensitive) - $condition[]=$prefix.$column->rawName.' LIKE '.$this->_connection->quoteValue('%'.$keyword.'%'); + $condition[]=$prefix.$column->rawName.' LIKE '.$this->_connection->quoteValue($keyword); else - $condition[]='LOWER('.$prefix.$column->rawName.') LIKE LOWER('.$this->_connection->quoteValue('%'.$keyword.'%').')'; + $condition[]='LOWER('.$prefix.$column->rawName.') LIKE LOWER('.$this->_connection->quoteValue($keyword).')'; } $conditions[]=implode(' AND ',$condition); } diff --git a/framework/db/schema/CDbCriteria.php b/framework/db/schema/CDbCriteria.php index 95061c483d4..86fb1b6085d 100644 --- a/framework/db/schema/CDbCriteria.php +++ b/framework/db/schema/CDbCriteria.php @@ -213,7 +213,7 @@ public function __wakeup() * After calling this method, the {@link condition} property will be modified. * @param mixed $condition the new condition. It can be either a string or an array of strings. * @param string $operator the operator to join different conditions. Defaults to 'AND'. - * @return CDbCriteria the criteria object itself + * @return static the criteria object itself */ public function addCondition($condition,$operator='AND') { @@ -246,7 +246,7 @@ public function addCondition($condition,$operator='AND') * @param string $operator the operator used to concatenate the new condition with the existing one. * Defaults to 'AND'. * @param string $like the LIKE operator. Defaults to 'LIKE'. You may also set this to be 'NOT LIKE'. - * @return CDbCriteria the criteria object itself + * @return static the criteria object itself */ public function addSearchCondition($column,$keyword,$escape=true,$operator='AND',$like='LIKE') { @@ -269,7 +269,7 @@ public function addSearchCondition($column,$keyword,$escape=true,$operator='AND' * @param array $values list of values that the column value should be in * @param string $operator the operator used to concatenate the new condition with the existing one. * Defaults to 'AND'. - * @return CDbCriteria the criteria object itself + * @return static the criteria object itself */ public function addInCondition($column,$values,$operator='AND') { @@ -309,7 +309,7 @@ public function addInCondition($column,$values,$operator='AND') * @param array $values list of values that the column value should not be in * @param string $operator the operator used to concatenate the new condition with the existing one. * Defaults to 'AND'. - * @return CDbCriteria the criteria object itself + * @return static the criteria object itself * @since 1.1.1 */ public function addNotInCondition($column,$values,$operator='AND') @@ -349,7 +349,7 @@ public function addNotInCondition($column,$values,$operator='AND') * @param string $columnOperator the operator to concatenate multiple column matching condition. Defaults to 'AND'. * @param string $operator the operator used to concatenate the new condition with the existing one. * Defaults to 'AND'. - * @return CDbCriteria the criteria object itself + * @return static the criteria object itself */ public function addColumnCondition($columns,$columnOperator='AND',$operator='AND') { @@ -408,7 +408,7 @@ public function addColumnCondition($columns,$columnOperator='AND',$operator='AND * and _ (matches a single character) will be escaped, and the value will be surrounded with a % * character on both ends. When this parameter is false, the value will be directly used for * matching without any change. - * @return CDbCriteria the criteria object itself + * @return static the criteria object itself * @since 1.1.1 */ public function compare($column, $value, $partialMatch=false, $operator='AND', $escape=true) @@ -462,7 +462,7 @@ public function compare($column, $value, $partialMatch=false, $operator='AND', $ * @param string $valueEnd the ending value to end the between search. * @param string $operator the operator used to concatenate the new condition with the existing one. * Defaults to 'AND'. - * @return CDbCriteria the criteria object itself + * @return static the criteria object itself * @since 1.1.2 */ public function addBetweenCondition($column,$valueStart,$valueEnd,$operator='AND') @@ -520,7 +520,7 @@ public function mergeWith($criteria,$operator='AND') if($this->params!==$criteria->params) $this->params=array_merge($this->params,$criteria->params); - if($criteria->limit>0) + if($criteria->limit>=0) $this->limit=$criteria->limit; if($criteria->offset>=0) diff --git a/framework/db/schema/CDbSchema.php b/framework/db/schema/CDbSchema.php index 8ea61ca960e..18d3f0e9326 100644 --- a/framework/db/schema/CDbSchema.php +++ b/framework/db/schema/CDbSchema.php @@ -316,10 +316,11 @@ protected function findTableNames($schema='') * These abstract column types are supported (using MySQL as example to explain the corresponding * physical types): * * @return array files found under the directory. The file list is sorted. */ @@ -108,8 +128,9 @@ public static function findFiles($dir,$options=array()) $fileTypes=array(); $exclude=array(); $level=-1; + $absolutePaths=true; extract($options); - $list=self::findFilesRecursive($dir,'',$fileTypes,$exclude,$level); + $list=self::findFilesRecursive($dir,'',$fileTypes,$exclude,$level,$absolutePaths); sort($list); return $list; } @@ -136,9 +157,11 @@ public static function findFiles($dir,$options=array()) protected static function copyDirectoryRecursive($src,$dst,$base,$fileTypes,$exclude,$level,$options) { if(!is_dir($dst)) - self::mkdir($dst,$options,false); + self::createDirectory($dst,isset($options['newDirMode'])?$options['newDirMode']:null,false); $folder=opendir($src); + if($folder===false) + throw new Exception('Unable to open directory: ' . $src); while(($file=readdir($folder))!==false) { if($file==='.' || $file==='..') @@ -174,24 +197,28 @@ protected static function copyDirectoryRecursive($src,$dst,$base,$fileTypes,$exc * Level -1 means searching for all directories and files under the directory; * Level 0 means searching for only the files DIRECTLY under the directory; * level N means searching for those directories that are within N levels. + * @param boolean $absolutePaths whether to return absolute paths or relative ones * @return array files found under the directory. */ - protected static function findFilesRecursive($dir,$base,$fileTypes,$exclude,$level) + protected static function findFilesRecursive($dir,$base,$fileTypes,$exclude,$level,$absolutePaths) { $list=array(); - $handle=opendir($dir); + $handle=opendir($dir.$base); + if($handle===false) + throw new Exception('Unable to open directory: ' . $dir); while(($file=readdir($handle))!==false) { if($file==='.' || $file==='..') continue; - $path=$dir.DIRECTORY_SEPARATOR.$file; - $isFile=is_file($path); + $path=substr($base.DIRECTORY_SEPARATOR.$file,1); + $fullPath=$dir.DIRECTORY_SEPARATOR.$path; + $isFile=is_file($fullPath); if(self::validatePath($base,$file,$isFile,$fileTypes,$exclude)) { if($isFile) - $list[]=$path; + $list[]=$absolutePaths?$fullPath:$path; elseif($level) - $list=array_merge($list,self::findFilesRecursive($path,$base.'/'.$file,$fileTypes,$exclude,$level-1)); + $list=array_merge($list,self::findFilesRecursive($dir,$base.'/'.$file,$fileTypes,$exclude,$level-1,$absolutePaths)); } } closedir($handle); @@ -219,7 +246,7 @@ protected static function validatePath($base,$file,$isFile,$fileTypes,$exclude) } if(!$isFile || empty($fileTypes)) return true; - if(($type=pathinfo($file,PATHINFO_EXTENSION))!=='') + if(($type=self::getExtension($file))!=='') return in_array($type,$fileTypes); else return false; @@ -276,7 +303,7 @@ public static function getMimeTypeByExtension($file,$magicFile=null) $extensions=require(Yii::getPathOfAlias('system.utils.mimeTypes').'.php'); elseif($magicFile!==null && !isset($customExtensions[$magicFile])) $customExtensions[$magicFile]=require($magicFile); - if(($ext=pathinfo($file,PATHINFO_EXTENSION))!=='') + if(($ext=self::getExtension($file))!=='') { $ext=strtolower($ext); if($magicFile===null && isset($extensions[$ext])) @@ -287,23 +314,50 @@ public static function getMimeTypeByExtension($file,$magicFile=null) return null; } + /** + * Determines the file extension name based on its MIME type. + * This method will use a local map between MIME type and extension name. + * @param string $file the file name. + * @param string $magicFile the path of the file that contains all available extension information. + * If this is not set, the default 'system.utils.fileExtensions' file will be used. + * This parameter has been available since version 1.1.16. + * @return string extension name. Null is returned if the extension cannot be determined. + */ + public static function getExtensionByMimeType($file,$magicFile=null) + { + static $mimeTypes,$customMimeTypes=array(); + if($magicFile===null && $mimeTypes===null) + $mimeTypes=require(Yii::getPathOfAlias('system.utils.fileExtensions').'.php'); + elseif($magicFile!==null && !isset($customMimeTypes[$magicFile])) + $customMimeTypes[$magicFile]=require($magicFile); + if(($mime=self::getMimeType($file))!==null) + { + $mime=strtolower($mime); + if($magicFile===null && isset($mimeTypes[$mime])) + return $mimeTypes[$mime]; + elseif($magicFile!==null && isset($customMimeTypes[$magicFile][$mime])) + return $customMimeTypes[$magicFile][$mime]; + } + return null; + } + /** * Shared environment safe version of mkdir. Supports recursive creation. * For avoidance of umask side-effects chmod is used. * * @param string $dst path to be created - * @param array $options newDirMode element used, must contain access bitmask + * @param integer $mode the permission to be set for newly created directories, if not set - 0777 will be used * @param boolean $recursive whether to create directory structure recursive if parent dirs do not exist * @return boolean result of mkdir * @see mkdir */ - private static function mkdir($dst,array $options,$recursive) + public static function createDirectory($dst,$mode=null,$recursive=false) { + if($mode===null) + $mode=0777; $prevDir=dirname($dst); if($recursive && !is_dir($dst) && !is_dir($prevDir)) - self::mkdir(dirname($dst),$options,true); - - $mode=isset($options['newDirMode']) ? $options['newDirMode'] : 0777; + self::createDirectory(dirname($dst),$mode,true); $res=mkdir($dst, $mode); @chmod($dst,$mode); return $res; diff --git a/framework/utils/CFormatter.php b/framework/utils/CFormatter.php index f072c366a75..4fbebb65e00 100644 --- a/framework/utils/CFormatter.php +++ b/framework/utils/CFormatter.php @@ -234,7 +234,10 @@ protected function normalizeDateValue($time) else return strtotime($time); } - return (int)$time; + elseif (class_exists('DateTime', false) && $time instanceof DateTime) + return $time->getTimestamp(); + else + return (int)$time; } /** diff --git a/framework/utils/CLocalizedFormatter.php b/framework/utils/CLocalizedFormatter.php index a2323f18042..b9df0bcb303 100644 --- a/framework/utils/CLocalizedFormatter.php +++ b/framework/utils/CLocalizedFormatter.php @@ -14,7 +14,7 @@ * It provides the same functionality as {@link CFormatter}, but overrides all the settings for * {@link booleanFormat}, {@link datetimeFormat} and {@link numberFormat} with the values for the * current locale. Because of this you are not able to configure these properties for CLocalizedFormatter directly. - * Date and time format can be adjsuted by setting {@link dateFormat} and {@link timeFormat}. + * Date and time format can be adjusted by setting {@link dateFormat} and {@link timeFormat}. * * It uses {@link CApplication::locale} by default but you can set a custom locale by using {@link setLocale}-method. * diff --git a/framework/utils/CPasswordHelper.php b/framework/utils/CPasswordHelper.php index 1949470d0b9..0ad581e3b41 100644 --- a/framework/utils/CPasswordHelper.php +++ b/framework/utils/CPasswordHelper.php @@ -33,7 +33,7 @@ *
      * $hash = CPasswordHelper::hashPassword($password);
      * 
    - * This hash can be stored in a database (e.g. CHAR(64) CHARACTER SET latin1). The + * This hash can be stored in a database (e.g. CHAR(60) CHARACTER SET latin1). The * hash is usually generated and saved to the database when the user enters a new password. * But it can also be useful to generate and save a hash after validating a user's * password in order to change the cost or refresh the salt. @@ -84,7 +84,7 @@ protected static function checkBlowfish() * compute the hash doubles for every increment by one of $cost. So, for example, if the * hash takes 1 second to compute when $cost is 14 then then the compute time varies as * 2^($cost - 14) seconds. - * @return string The password hash string, ASCII and not longer than 64 characters. + * @return string The password hash string, always 60 ASCII characters. * @throws CException on bad password parameter or if crypt() with Blowfish hash is not available. */ public static function hashPassword($password,$cost=13) @@ -167,7 +167,7 @@ public static function same($a,$b) * * The PHP {@link http://php.net/manual/en/function.crypt.php crypt()} built-in function * requires, for the Blowfish hash algorithm, a salt string in a specific format: - * "$2a$" (in which the "a" may be replaced by "x" or "y" see PHP manual for details), + * "$2y$" (in which the "y" may be replaced by "a" or "y" see PHP manual for details), * a two digit cost parameter, * "$", * 22 characters from the alphabet "./0-9A-Za-z". @@ -188,6 +188,6 @@ public static function generateSalt($cost=13) if(($random=Yii::app()->getSecurityManager()->generateRandomString(22,true))===false) if(($random=Yii::app()->getSecurityManager()->generateRandomString(22,false))===false) throw new CException(Yii::t('yii','Unable to generate random string.')); - return sprintf('$2a$%02d$',$cost).strtr($random,array('_'=>'.','~'=>'/')); + return sprintf('$2y$%02d$',$cost).strtr($random,array('_'=>'.','~'=>'/')); } } diff --git a/framework/utils/mimeTypes.php b/framework/utils/mimeTypes.php index 25822fc3e1c..d81f9e338b5 100644 --- a/framework/utils/mimeTypes.php +++ b/framework/utils/mimeTypes.php @@ -4,185 +4,992 @@ * * This file contains most commonly used MIME types * according to file extension names. + * Its content is generated from the apache http mime.types file. + * http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?view=markup + * This file has been placed in the public domain for unlimited redistribution. * - * @author Qiang Xue - * @link http://www.yiiframework.com/ - * @copyright 2008-2013 Yii Software LLC - * @license http://www.yiiframework.com/license/ + * This file has been backported form Yii 2. */ - return array( + '3dml'=>'text/vnd.in3d.3dml', + '3ds'=>'image/x-3ds', + '3g2'=>'video/3gpp2', + '3gp'=>'video/3gpp', + '7z'=>'application/x-7z-compressed', + 'aab'=>'application/x-authorware-bin', + 'aac'=>'audio/x-aac', + 'aam'=>'application/x-authorware-map', + 'aas'=>'application/x-authorware-seg', + 'abw'=>'application/x-abiword', + 'ac'=>'application/pkix-attr-cert', + 'acc'=>'application/vnd.americandynamics.acc', + 'ace'=>'application/x-ace-compressed', + 'acu'=>'application/vnd.acucobol', + 'acutc'=>'application/vnd.acucorp', + 'adp'=>'audio/adpcm', + 'aep'=>'application/vnd.audiograph', + 'afm'=>'application/x-font-type1', + 'afp'=>'application/vnd.ibm.modcap', + 'ahead'=>'application/vnd.ahead.space', 'ai'=>'application/postscript', 'aif'=>'audio/x-aiff', 'aifc'=>'audio/x-aiff', 'aiff'=>'audio/x-aiff', - 'anx'=>'application/annodex', - 'asc'=>'text/plain', + 'air'=>'application/vnd.adobe.air-application-installer-package+zip', + 'ait'=>'application/vnd.dvb.ait', + 'ami'=>'application/vnd.amiga.ami', + 'apk'=>'application/vnd.android.package-archive', + 'appcache'=>'text/cache-manifest', + 'application'=>'application/x-ms-application', + 'apr'=>'application/vnd.lotus-approach', + 'arc'=>'application/x-freearc', + 'asc'=>'application/pgp-signature', + 'asf'=>'video/x-ms-asf', + 'asm'=>'text/x-asm', + 'aso'=>'application/vnd.accpac.simply.aso', + 'asx'=>'video/x-ms-asf', + 'atc'=>'application/vnd.acucorp', + 'atom'=>'application/atom+xml', + 'atomcat'=>'application/atomcat+xml', + 'atomsvc'=>'application/atomsvc+xml', + 'atx'=>'application/vnd.antix.game-component', 'au'=>'audio/basic', 'avi'=>'video/x-msvideo', - 'axa'=>'audio/annodex', - 'axv'=>'video/annodex', + 'aw'=>'application/applixware', + 'azf'=>'application/vnd.airzip.filesecure.azf', + 'azs'=>'application/vnd.airzip.filesecure.azs', + 'azw'=>'application/vnd.amazon.ebook', + 'bat'=>'application/x-msdownload', 'bcpio'=>'application/x-bcpio', + 'bdf'=>'application/x-font-bdf', + 'bdm'=>'application/vnd.syncml.dm+wbxml', + 'bed'=>'application/vnd.realvnc.bed', + 'bh2'=>'application/vnd.fujitsu.oasysprs', 'bin'=>'application/octet-stream', + 'blb'=>'application/x-blorb', + 'blorb'=>'application/x-blorb', + 'bmi'=>'application/vnd.bmi', 'bmp'=>'image/bmp', - 'c'=>'text/plain', - 'cc'=>'text/plain', - 'ccad'=>'application/clariscad', + 'book'=>'application/vnd.framemaker', + 'box'=>'application/vnd.previewsystems.box', + 'boz'=>'application/x-bzip2', + 'bpk'=>'application/octet-stream', + 'btif'=>'image/prs.btif', + 'bz'=>'application/x-bzip', + 'bz2'=>'application/x-bzip2', + 'c'=>'text/x-c', + 'c11amc'=>'application/vnd.cluetrust.cartomobile-config', + 'c11amz'=>'application/vnd.cluetrust.cartomobile-config-pkg', + 'c4d'=>'application/vnd.clonk.c4group', + 'c4f'=>'application/vnd.clonk.c4group', + 'c4g'=>'application/vnd.clonk.c4group', + 'c4p'=>'application/vnd.clonk.c4group', + 'c4u'=>'application/vnd.clonk.c4group', + 'cab'=>'application/vnd.ms-cab-compressed', + 'caf'=>'audio/x-caf', + 'cap'=>'application/vnd.tcpdump.pcap', + 'car'=>'application/vnd.curl.car', + 'cat'=>'application/vnd.ms-pki.seccat', + 'cb7'=>'application/x-cbr', + 'cba'=>'application/x-cbr', + 'cbr'=>'application/x-cbr', + 'cbt'=>'application/x-cbr', + 'cbz'=>'application/x-cbr', + 'cc'=>'text/x-c', + 'cct'=>'application/x-director', + 'ccxml'=>'application/ccxml+xml', + 'cdbcmsg'=>'application/vnd.contact.cmsg', 'cdf'=>'application/x-netcdf', - 'class'=>'application/octet-stream', + 'cdkey'=>'application/vnd.mediastation.cdkey', + 'cdmia'=>'application/cdmi-capability', + 'cdmic'=>'application/cdmi-container', + 'cdmid'=>'application/cdmi-domain', + 'cdmio'=>'application/cdmi-object', + 'cdmiq'=>'application/cdmi-queue', + 'cdx'=>'chemical/x-cdx', + 'cdxml'=>'application/vnd.chemdraw+xml', + 'cdy'=>'application/vnd.cinderella', + 'cer'=>'application/pkix-cert', + 'cfs'=>'application/x-cfs-compressed', + 'cgm'=>'image/cgm', + 'chat'=>'application/x-chat', + 'chm'=>'application/vnd.ms-htmlhelp', + 'chrt'=>'application/vnd.kde.kchart', + 'cif'=>'chemical/x-cif', + 'cii'=>'application/vnd.anser-web-certificate-issue-initiation', + 'cil'=>'application/vnd.ms-artgalry', + 'cla'=>'application/vnd.claymore', + 'class'=>'application/java-vm', + 'clkk'=>'application/vnd.crick.clicker.keyboard', + 'clkp'=>'application/vnd.crick.clicker.palette', + 'clkt'=>'application/vnd.crick.clicker.template', + 'clkw'=>'application/vnd.crick.clicker.wordbank', + 'clkx'=>'application/vnd.crick.clicker', + 'clp'=>'application/x-msclip', + 'cmc'=>'application/vnd.cosmocaller', + 'cmdf'=>'chemical/x-cmdf', + 'cml'=>'chemical/x-cml', + 'cmp'=>'application/vnd.yellowriver-custom-menu', + 'cmx'=>'image/x-cmx', + 'cod'=>'application/vnd.rim.cod', + 'com'=>'application/x-msdownload', + 'conf'=>'text/plain', 'cpio'=>'application/x-cpio', + 'cpp'=>'text/x-c', 'cpt'=>'application/mac-compactpro', + 'crd'=>'application/x-mscardfile', + 'crl'=>'application/pkix-crl', + 'crt'=>'application/x-x509-ca-cert', + 'cryptonote'=>'application/vnd.rig.cryptonote', 'csh'=>'application/x-csh', + 'csml'=>'chemical/x-csml', + 'csp'=>'application/vnd.commonspace', 'css'=>'text/css', + 'cst'=>'application/x-director', 'csv'=>'text/csv', + 'cu'=>'application/cu-seeme', + 'curl'=>'text/vnd.curl', + 'cww'=>'application/prs.cww', + 'cxt'=>'application/x-director', + 'cxx'=>'text/x-c', + 'dae'=>'model/vnd.collada+xml', + 'daf'=>'application/vnd.mobius.daf', + 'dart'=>'application/vnd.dart', + 'dataless'=>'application/vnd.fdsn.seed', + 'davmount'=>'application/davmount+xml', + 'dbk'=>'application/docbook+xml', 'dcr'=>'application/x-director', + 'dcurl'=>'text/vnd.curl.dcurl', + 'dd2'=>'application/vnd.oma.dd2+xml', + 'ddd'=>'application/vnd.fujixerox.ddd', + 'deb'=>'application/x-debian-package', + 'def'=>'text/plain', + 'deploy'=>'application/octet-stream', + 'der'=>'application/x-x509-ca-cert', + 'dfac'=>'application/vnd.dreamfactory', + 'dgc'=>'application/x-dgc-compressed', + 'dic'=>'text/x-c', 'dir'=>'application/x-director', + 'dis'=>'application/vnd.mobius.dis', + 'dist'=>'application/octet-stream', + 'distz'=>'application/octet-stream', + 'djv'=>'image/vnd.djvu', + 'djvu'=>'image/vnd.djvu', + 'dll'=>'application/x-msdownload', + 'dmg'=>'application/x-apple-diskimage', + 'dmp'=>'application/vnd.tcpdump.pcap', 'dms'=>'application/octet-stream', + 'dna'=>'application/vnd.dna', 'doc'=>'application/msword', - 'drw'=>'application/drafting', + 'docm'=>'application/vnd.ms-word.document.macroenabled.12', + 'docx'=>'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot'=>'application/msword', + 'dotm'=>'application/vnd.ms-word.template.macroenabled.12', + 'dotx'=>'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp'=>'application/vnd.osgi.dp', + 'dpg'=>'application/vnd.dpgraph', + 'dra'=>'audio/vnd.dra', + 'dsc'=>'text/prs.lines.tag', + 'dssc'=>'application/dssc+der', + 'dtb'=>'application/x-dtbook+xml', + 'dtd'=>'application/xml-dtd', + 'dts'=>'audio/vnd.dts', + 'dtshd'=>'audio/vnd.dts.hd', + 'dump'=>'application/octet-stream', + 'dvb'=>'video/vnd.dvb.file', 'dvi'=>'application/x-dvi', - 'dwg'=>'application/acad', - 'dxf'=>'application/dxf', + 'dwf'=>'model/vnd.dwf', + 'dwg'=>'image/vnd.dwg', + 'dxf'=>'image/vnd.dxf', + 'dxp'=>'application/vnd.spotfire.dxp', 'dxr'=>'application/x-director', + 'ecelp4800'=>'audio/vnd.nuera.ecelp4800', + 'ecelp7470'=>'audio/vnd.nuera.ecelp7470', + 'ecelp9600'=>'audio/vnd.nuera.ecelp9600', + 'ecma'=>'application/ecmascript', + 'edm'=>'application/vnd.novadigm.edm', + 'edx'=>'application/vnd.novadigm.edx', + 'efif'=>'application/vnd.picsel', + 'ei6'=>'application/vnd.pg.osasli', + 'elc'=>'application/octet-stream', + 'emf'=>'application/x-msmetafile', + 'eml'=>'message/rfc822', + 'emma'=>'application/emma+xml', + 'emz'=>'application/x-msmetafile', + 'eol'=>'audio/vnd.digital-winds', + 'eot'=>'application/vnd.ms-fontobject', 'eps'=>'application/postscript', + 'epub'=>'application/epub+zip', + 'es3'=>'application/vnd.eszigno3+xml', + 'esa'=>'application/vnd.osgi.subsystem', + 'esf'=>'application/vnd.epson.esf', + 'et3'=>'application/vnd.eszigno3+xml', 'etx'=>'text/x-setext', - 'exe'=>'application/octet-stream', + 'eva'=>'application/x-eva', + 'evy'=>'application/x-envoy', + 'exe'=>'application/x-msdownload', + 'exi'=>'application/exi', + 'ext'=>'application/vnd.novadigm.ext', 'ez'=>'application/andrew-inset', - 'f'=>'text/plain', - 'f90'=>'text/plain', - 'flac'=>'audio/flac', + 'ez2'=>'application/vnd.ezpix-album', + 'ez3'=>'application/vnd.ezpix-package', + 'f'=>'text/x-fortran', + 'f4v'=>'video/x-f4v', + 'f77'=>'text/x-fortran', + 'f90'=>'text/x-fortran', + 'fbs'=>'image/vnd.fastbidsheet', + 'fcdt'=>'application/vnd.adobe.formscentral.fcdt', + 'fcs'=>'application/vnd.isac.fcs', + 'fdf'=>'application/vnd.fdf', + 'fe_launch'=>'application/vnd.denovo.fcselayout-link', + 'fg5'=>'application/vnd.fujitsu.oasysgp', + 'fgd'=>'application/x-director', + 'fh'=>'image/x-freehand', + 'fh4'=>'image/x-freehand', + 'fh5'=>'image/x-freehand', + 'fh7'=>'image/x-freehand', + 'fhc'=>'image/x-freehand', + 'fig'=>'application/x-xfig', + 'flac'=>'audio/x-flac', 'fli'=>'video/x-fli', + 'flo'=>'application/vnd.micrografx.flo', 'flv'=>'video/x-flv', + 'flw'=>'application/vnd.kde.kivio', + 'flx'=>'text/vnd.fmi.flexstor', + 'fly'=>'text/vnd.fly', + 'fm'=>'application/vnd.framemaker', + 'fnc'=>'application/vnd.frogans.fnc', + 'for'=>'text/x-fortran', + 'fpx'=>'image/vnd.fpx', + 'frame'=>'application/vnd.framemaker', + 'fsc'=>'application/vnd.fsc.weblaunch', + 'fst'=>'image/vnd.fst', + 'ftc'=>'application/vnd.fluxtime.clip', + 'fti'=>'application/vnd.anser-web-funds-transfer-initiation', + 'fvt'=>'video/vnd.fvt', + 'fxp'=>'application/vnd.adobe.fxp', + 'fxpl'=>'application/vnd.adobe.fxp', + 'fzs'=>'application/vnd.fuzzysheet', + 'g2w'=>'application/vnd.geoplan', + 'g3'=>'image/g3fax', + 'g3w'=>'application/vnd.geospace', + 'gac'=>'application/vnd.groove-account', + 'gam'=>'application/x-tads', + 'gbr'=>'application/rpki-ghostbusters', + 'gca'=>'application/x-gca-compressed', + 'gdl'=>'model/vnd.gdl', + 'geo'=>'application/vnd.dynageo', + 'gex'=>'application/vnd.geometry-explorer', + 'ggb'=>'application/vnd.geogebra.file', + 'ggt'=>'application/vnd.geogebra.tool', + 'ghf'=>'application/vnd.groove-help', 'gif'=>'image/gif', + 'gim'=>'application/vnd.groove-identity-message', + 'gml'=>'application/gml+xml', + 'gmx'=>'application/vnd.gmx', + 'gnumeric'=>'application/x-gnumeric', + 'gph'=>'application/vnd.flographit', + 'gpx'=>'application/gpx+xml', + 'gqf'=>'application/vnd.grafeq', + 'gqs'=>'application/vnd.grafeq', + 'gram'=>'application/srgs', + 'gramps'=>'application/x-gramps-xml', + 'gre'=>'application/vnd.geometry-explorer', + 'grv'=>'application/vnd.groove-injector', + 'grxml'=>'application/srgs+xml', + 'gsf'=>'application/x-font-ghostscript', 'gtar'=>'application/x-gtar', - 'gz'=>'application/x-gzip', - 'h'=>'text/plain', + 'gtm'=>'application/vnd.groove-tool-message', + 'gtw'=>'model/vnd.gtw', + 'gv'=>'text/vnd.graphviz', + 'gxf'=>'application/gxf', + 'gxt'=>'application/vnd.geonext', + 'h'=>'text/x-c', + 'h261'=>'video/h261', + 'h263'=>'video/h263', + 'h264'=>'video/h264', + 'hal'=>'application/vnd.hal+xml', + 'hbci'=>'application/vnd.hbci', 'hdf'=>'application/x-hdf', - 'hh'=>'text/plain', + 'hh'=>'text/x-c', + 'hlp'=>'application/winhlp', + 'hpgl'=>'application/vnd.hp-hpgl', + 'hpid'=>'application/vnd.hp-hpid', + 'hps'=>'application/vnd.hp-hps', 'hqx'=>'application/mac-binhex40', + 'htke'=>'application/vnd.kenameaapp', 'htm'=>'text/html', 'html'=>'text/html', + 'hvd'=>'application/vnd.yamaha.hv-dic', + 'hvp'=>'application/vnd.yamaha.hv-voice', + 'hvs'=>'application/vnd.yamaha.hv-script', + 'i2g'=>'application/vnd.intergeo', + 'icc'=>'application/vnd.iccprofile', 'ice'=>'x-conference/x-cooltalk', + 'icm'=>'application/vnd.iccprofile', + 'ico'=>'image/x-icon', + 'ics'=>'text/calendar', 'ief'=>'image/ief', + 'ifb'=>'text/calendar', + 'ifm'=>'application/vnd.shana.informed.formdata', 'iges'=>'model/iges', + 'igl'=>'application/vnd.igloader', + 'igm'=>'application/vnd.insors.igm', 'igs'=>'model/iges', - 'ips'=>'application/x-ipscript', - 'ipx'=>'application/x-ipix', + 'igx'=>'application/vnd.micrografx.igx', + 'iif'=>'application/vnd.shana.informed.interchange', + 'imp'=>'application/vnd.accpac.simply.imp', + 'ims'=>'application/vnd.ms-ims', + 'in'=>'text/plain', + 'ink'=>'application/inkml+xml', + 'inkml'=>'application/inkml+xml', + 'install'=>'application/x-install-instructions', + 'iota'=>'application/vnd.astraea-software.iota', + 'ipfix'=>'application/ipfix', + 'ipk'=>'application/vnd.shana.informed.package', + 'irm'=>'application/vnd.ibm.rights-management', + 'irp'=>'application/vnd.irepository.package+xml', + 'iso'=>'application/x-iso9660-image', + 'itp'=>'application/vnd.shana.informed.formtemplate', + 'ivp'=>'application/vnd.immervision-ivp', + 'ivu'=>'application/vnd.immervision-ivu', + 'jad'=>'text/vnd.sun.j2me.app-descriptor', + 'jam'=>'application/vnd.jam', + 'jar'=>'application/java-archive', + 'java'=>'text/x-java-source', + 'jisp'=>'application/vnd.jisp', + 'jlt'=>'application/vnd.hp-jlyt', + 'jnlp'=>'application/x-java-jnlp-file', + 'joda'=>'application/vnd.joost.joda-archive', 'jpe'=>'image/jpeg', 'jpeg'=>'image/jpeg', 'jpg'=>'image/jpeg', - 'js'=>'application/x-javascript', + 'jpgm'=>'video/jpm', + 'jpgv'=>'video/jpeg', + 'jpm'=>'video/jpm', + 'js'=>'application/javascript', + 'json'=>'application/json', + 'jsonml'=>'application/jsonml+json', 'kar'=>'audio/midi', + 'karbon'=>'application/vnd.kde.karbon', + 'kfo'=>'application/vnd.kde.kformula', + 'kia'=>'application/vnd.kidspiration', + 'kml'=>'application/vnd.google-earth.kml+xml', + 'kmz'=>'application/vnd.google-earth.kmz', + 'kne'=>'application/vnd.kinar', + 'knp'=>'application/vnd.kinar', + 'kon'=>'application/vnd.kde.kontour', + 'kpr'=>'application/vnd.kde.kpresenter', + 'kpt'=>'application/vnd.kde.kpresenter', + 'kpxx'=>'application/vnd.ds-keypoint', + 'ksp'=>'application/vnd.kde.kspread', + 'ktr'=>'application/vnd.kahootz', + 'ktx'=>'image/ktx', + 'ktz'=>'application/vnd.kahootz', + 'kwd'=>'application/vnd.kde.kword', + 'kwt'=>'application/vnd.kde.kword', + 'lasxml'=>'application/vnd.las.las+xml', 'latex'=>'application/x-latex', - 'lha'=>'application/octet-stream', - 'lsp'=>'application/x-lisp', - 'lzh'=>'application/octet-stream', - 'm'=>'text/plain', - 'man'=>'application/x-troff-man', - 'me'=>'application/x-troff-me', + 'lbd'=>'application/vnd.llamagraphics.life-balance.desktop', + 'lbe'=>'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les'=>'application/vnd.hhe.lesson-player', + 'lha'=>'application/x-lzh-compressed', + 'link66'=>'application/vnd.route66.link66+xml', + 'list'=>'text/plain', + 'list3820'=>'application/vnd.ibm.modcap', + 'listafp'=>'application/vnd.ibm.modcap', + 'lnk'=>'application/x-ms-shortcut', + 'log'=>'text/plain', + 'lostxml'=>'application/lost+xml', + 'lrf'=>'application/octet-stream', + 'lrm'=>'application/vnd.ms-lrm', + 'ltf'=>'application/vnd.frogans.ltf', + 'lvp'=>'audio/vnd.lucent.voice', + 'lwp'=>'application/vnd.lotus-wordpro', + 'lzh'=>'application/x-lzh-compressed', + 'm13'=>'application/x-msmediaview', + 'm14'=>'application/x-msmediaview', + 'm1v'=>'video/mpeg', + 'm21'=>'application/mp21', + 'm2a'=>'audio/mpeg', + 'm2v'=>'video/mpeg', + 'm3a'=>'audio/mpeg', + 'm3u'=>'audio/x-mpegurl', + 'm3u8'=>'application/vnd.apple.mpegurl', + 'm4u'=>'video/vnd.mpegurl', + 'm4v'=>'video/x-m4v', + 'ma'=>'application/mathematica', + 'mads'=>'application/mads+xml', + 'mag'=>'application/vnd.ecowin.chart', + 'maker'=>'application/vnd.framemaker', + 'man'=>'text/troff', + 'mar'=>'application/octet-stream', + 'mathml'=>'application/mathml+xml', + 'mb'=>'application/mathematica', + 'mbk'=>'application/vnd.mobius.mbk', + 'mbox'=>'application/mbox', + 'mc1'=>'application/vnd.medcalcdata', + 'mcd'=>'application/vnd.mcd', + 'mcurl'=>'text/vnd.curl.mcurl', + 'mdb'=>'application/x-msaccess', + 'mdi'=>'image/vnd.ms-modi', + 'me'=>'text/troff', 'mesh'=>'model/mesh', + 'meta4'=>'application/metalink4+xml', + 'metalink'=>'application/metalink+xml', + 'mets'=>'application/mets+xml', + 'mfm'=>'application/vnd.mfmp', + 'mft'=>'application/rpki-manifest', + 'mgp'=>'application/vnd.osgeo.mapguide.package', + 'mgz'=>'application/vnd.proteus.magazine', 'mid'=>'audio/midi', 'midi'=>'audio/midi', + 'mie'=>'application/x-mie', 'mif'=>'application/vnd.mif', - 'mime'=>'www/mime', + 'mime'=>'message/rfc822', + 'mj2'=>'video/mj2', + 'mjp2'=>'video/mj2', + 'mk3d'=>'video/x-matroska', + 'mka'=>'audio/x-matroska', + 'mks'=>'video/x-matroska', + 'mkv'=>'video/x-matroska', + 'mlp'=>'application/vnd.dolby.mlp', + 'mmd'=>'application/vnd.chipnuts.karaoke-mmd', + 'mmf'=>'application/vnd.smaf', + 'mmr'=>'image/vnd.fujixerox.edmics-mmr', + 'mng'=>'video/x-mng', + 'mny'=>'application/x-msmoney', + 'mobi'=>'application/x-mobipocket-ebook', + 'mods'=>'application/mods+xml', 'mov'=>'video/quicktime', 'movie'=>'video/x-sgi-movie', 'mp2'=>'audio/mpeg', + 'mp21'=>'application/mp21', + 'mp2a'=>'audio/mpeg', 'mp3'=>'audio/mpeg', + 'mp4'=>'video/mp4', + 'mp4a'=>'audio/mp4', + 'mp4s'=>'application/mp4', + 'mp4v'=>'video/mp4', + 'mpc'=>'application/vnd.mophun.certificate', 'mpe'=>'video/mpeg', 'mpeg'=>'video/mpeg', 'mpg'=>'video/mpeg', + 'mpg4'=>'video/mp4', 'mpga'=>'audio/mpeg', - 'ms'=>'application/x-troff-ms', + 'mpkg'=>'application/vnd.apple.installer+xml', + 'mpm'=>'application/vnd.blueice.multipass', + 'mpn'=>'application/vnd.mophun.application', + 'mpp'=>'application/vnd.ms-project', + 'mpt'=>'application/vnd.ms-project', + 'mpy'=>'application/vnd.ibm.minipay', + 'mqy'=>'application/vnd.mobius.mqy', + 'mrc'=>'application/marc', + 'mrcx'=>'application/marcxml+xml', + 'ms'=>'text/troff', + 'mscml'=>'application/mediaservercontrol+xml', + 'mseed'=>'application/vnd.fdsn.mseed', + 'mseq'=>'application/vnd.mseq', + 'msf'=>'application/vnd.epson.msf', 'msh'=>'model/mesh', + 'msi'=>'application/x-msdownload', + 'msl'=>'application/vnd.mobius.msl', + 'msty'=>'application/vnd.muvee.style', + 'mts'=>'model/vnd.mts', + 'mus'=>'application/vnd.musician', + 'musicxml'=>'application/vnd.recordare.musicxml+xml', + 'mvb'=>'application/x-msmediaview', + 'mwf'=>'application/vnd.mfer', + 'mxf'=>'application/mxf', + 'mxl'=>'application/vnd.recordare.musicxml', + 'mxml'=>'application/xv+xml', + 'mxs'=>'application/vnd.triscape.mxs', + 'mxu'=>'video/vnd.mpegurl', + 'n-gage'=>'application/vnd.nokia.n-gage.symbian.install', + 'n3'=>'text/n3', + 'nb'=>'application/mathematica', + 'nbp'=>'application/vnd.wolfram.player', 'nc'=>'application/x-netcdf', + 'ncx'=>'application/x-dtbncx+xml', + 'nfo'=>'text/x-nfo', + 'ngdat'=>'application/vnd.nokia.n-gage.data', + 'nitf'=>'application/vnd.nitf', + 'nlu'=>'application/vnd.neurolanguage.nlu', + 'nml'=>'application/vnd.enliven', + 'nnd'=>'application/vnd.noblenet-directory', + 'nns'=>'application/vnd.noblenet-sealer', + 'nnw'=>'application/vnd.noblenet-web', + 'npx'=>'image/vnd.net-fpx', + 'nsc'=>'application/x-conference', + 'nsf'=>'application/vnd.lotus-notes', + 'ntf'=>'application/vnd.nitf', + 'nzb'=>'application/x-nzb', + 'oa2'=>'application/vnd.fujitsu.oasys2', + 'oa3'=>'application/vnd.fujitsu.oasys3', + 'oas'=>'application/vnd.fujitsu.oasys', + 'obd'=>'application/x-msbinder', + 'obj'=>'application/x-tgif', + 'oda'=>'application/oda', + 'odb'=>'application/vnd.oasis.opendocument.database', + 'odc'=>'application/vnd.oasis.opendocument.chart', + 'odf'=>'application/vnd.oasis.opendocument.formula', + 'odft'=>'application/vnd.oasis.opendocument.formula-template', + 'odg'=>'application/vnd.oasis.opendocument.graphics', + 'odi'=>'application/vnd.oasis.opendocument.image', + 'odm'=>'application/vnd.oasis.opendocument.text-master', + 'odp'=>'application/vnd.oasis.opendocument.presentation', + 'ods'=>'application/vnd.oasis.opendocument.spreadsheet', + 'odt'=>'application/vnd.oasis.opendocument.text', 'oga'=>'audio/ogg', 'ogg'=>'audio/ogg', 'ogv'=>'video/ogg', 'ogx'=>'application/ogg', - 'oda'=>'application/oda', + 'omdoc'=>'application/omdoc+xml', + 'onepkg'=>'application/onenote', + 'onetmp'=>'application/onenote', + 'onetoc'=>'application/onenote', + 'onetoc2'=>'application/onenote', + 'opf'=>'application/oebps-package+xml', + 'opml'=>'text/x-opml', + 'oprc'=>'application/vnd.palm', + 'org'=>'application/vnd.lotus-organizer', + 'osf'=>'application/vnd.yamaha.openscoreformat', + 'osfpvg'=>'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'otc'=>'application/vnd.oasis.opendocument.chart-template', + 'otf'=>'application/x-font-otf', + 'otg'=>'application/vnd.oasis.opendocument.graphics-template', + 'oth'=>'application/vnd.oasis.opendocument.text-web', + 'oti'=>'application/vnd.oasis.opendocument.image-template', + 'otp'=>'application/vnd.oasis.opendocument.presentation-template', + 'ots'=>'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott'=>'application/vnd.oasis.opendocument.text-template', + 'oxps'=>'application/oxps', + 'oxt'=>'application/vnd.openofficeorg.extension', + 'p'=>'text/x-pascal', + 'p10'=>'application/pkcs10', + 'p12'=>'application/x-pkcs12', + 'p7b'=>'application/x-pkcs7-certificates', + 'p7c'=>'application/pkcs7-mime', + 'p7m'=>'application/pkcs7-mime', + 'p7r'=>'application/x-pkcs7-certreqresp', + 'p7s'=>'application/pkcs7-signature', + 'p8'=>'application/pkcs8', + 'pas'=>'text/x-pascal', + 'paw'=>'application/vnd.pawaafile', + 'pbd'=>'application/vnd.powerbuilder6', 'pbm'=>'image/x-portable-bitmap', - 'pdb'=>'chemical/x-pdb', + 'pcap'=>'application/vnd.tcpdump.pcap', + 'pcf'=>'application/x-font-pcf', + 'pcl'=>'application/vnd.hp-pcl', + 'pclxl'=>'application/vnd.hp-pclxl', + 'pct'=>'image/x-pict', + 'pcurl'=>'application/vnd.curl.pcurl', + 'pcx'=>'image/x-pcx', + 'pdb'=>'application/vnd.palm', 'pdf'=>'application/pdf', + 'pfa'=>'application/x-font-type1', + 'pfb'=>'application/x-font-type1', + 'pfm'=>'application/x-font-type1', + 'pfr'=>'application/font-tdpfr', + 'pfx'=>'application/x-pkcs12', 'pgm'=>'image/x-portable-graymap', 'pgn'=>'application/x-chess-pgn', + 'pgp'=>'application/pgp-encrypted', + 'pic'=>'image/x-pict', + 'pkg'=>'application/octet-stream', + 'pki'=>'application/pkixcmp', + 'pkipath'=>'application/pkix-pkipath', + 'plb'=>'application/vnd.3gpp.pic-bw-large', + 'plc'=>'application/vnd.mobius.plc', + 'plf'=>'application/vnd.pocketlearn', + 'pls'=>'application/pls+xml', + 'pml'=>'application/vnd.ctc-posml', 'png'=>'image/png', 'pnm'=>'image/x-portable-anymap', - 'pot'=>'application/mspowerpoint', + 'portpkg'=>'application/vnd.macports.portpkg', + 'pot'=>'application/vnd.ms-powerpoint', + 'potm'=>'application/vnd.ms-powerpoint.template.macroenabled.12', + 'potx'=>'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppam'=>'application/vnd.ms-powerpoint.addin.macroenabled.12', + 'ppd'=>'application/vnd.cups-ppd', 'ppm'=>'image/x-portable-pixmap', - 'pps'=>'application/mspowerpoint', - 'ppt'=>'application/mspowerpoint', - 'ppz'=>'application/mspowerpoint', - 'pre'=>'application/x-freelance', - 'prt'=>'application/pro_eng', + 'pps'=>'application/vnd.ms-powerpoint', + 'ppsm'=>'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + 'ppsx'=>'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt'=>'application/vnd.ms-powerpoint', + 'pptm'=>'application/vnd.ms-powerpoint.presentation.macroenabled.12', + 'pptx'=>'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa'=>'application/vnd.palm', + 'prc'=>'application/x-mobipocket-ebook', + 'pre'=>'application/vnd.lotus-freelance', + 'prf'=>'application/pics-rules', 'ps'=>'application/postscript', + 'psb'=>'application/vnd.3gpp.pic-bw-small', + 'psd'=>'image/vnd.adobe.photoshop', + 'psf'=>'application/x-font-linux-psf', + 'pskcxml'=>'application/pskc+xml', + 'ptid'=>'application/vnd.pvi.ptid1', + 'pub'=>'application/x-mspublisher', + 'pvb'=>'application/vnd.3gpp.pic-bw-var', + 'pwn'=>'application/vnd.3m.post-it-notes', + 'pya'=>'audio/vnd.ms-playready.media.pya', + 'pyv'=>'video/vnd.ms-playready.media.pyv', + 'qam'=>'application/vnd.epson.quickanime', + 'qbo'=>'application/vnd.intu.qbo', + 'qfx'=>'application/vnd.intu.qfx', + 'qps'=>'application/vnd.publishare-delta-tree', 'qt'=>'video/quicktime', - 'ra'=>'audio/x-realaudio', + 'qwd'=>'application/vnd.quark.quarkxpress', + 'qwt'=>'application/vnd.quark.quarkxpress', + 'qxb'=>'application/vnd.quark.quarkxpress', + 'qxd'=>'application/vnd.quark.quarkxpress', + 'qxl'=>'application/vnd.quark.quarkxpress', + 'qxt'=>'application/vnd.quark.quarkxpress', + 'ra'=>'audio/x-pn-realaudio', 'ram'=>'audio/x-pn-realaudio', - 'ras'=>'image/cmu-raster', + 'rar'=>'application/x-rar-compressed', + 'ras'=>'image/x-cmu-raster', + 'rcprofile'=>'application/vnd.ipunplugged.rcprofile', + 'rdf'=>'application/rdf+xml', + 'rdz'=>'application/vnd.data-vision.rdz', + 'rep'=>'application/vnd.businessobjects', + 'res'=>'application/x-dtbresource+xml', 'rgb'=>'image/x-rgb', - 'rm'=>'audio/x-pn-realaudio', - 'roff'=>'application/x-troff', - 'rpm'=>'audio/x-pn-realaudio-plugin', - 'rtf'=>'text/rtf', + 'rif'=>'application/reginfo+xml', + 'rip'=>'audio/vnd.rip', + 'ris'=>'application/x-research-info-systems', + 'rl'=>'application/resource-lists+xml', + 'rlc'=>'image/vnd.fujixerox.edmics-rlc', + 'rld'=>'application/resource-lists-diff+xml', + 'rm'=>'application/vnd.rn-realmedia', + 'rmi'=>'audio/midi', + 'rmp'=>'audio/x-pn-realaudio-plugin', + 'rms'=>'application/vnd.jcp.javame.midlet-rms', + 'rmvb'=>'application/vnd.rn-realmedia-vbr', + 'rnc'=>'application/relax-ng-compact-syntax', + 'roa'=>'application/rpki-roa', + 'roff'=>'text/troff', + 'rp9'=>'application/vnd.cloanto.rp9', + 'rpss'=>'application/vnd.nokia.radio-presets', + 'rpst'=>'application/vnd.nokia.radio-preset', + 'rq'=>'application/sparql-query', + 'rs'=>'application/rls-services+xml', + 'rsd'=>'application/rsd+xml', + 'rss'=>'application/rss+xml', + 'rtf'=>'application/rtf', 'rtx'=>'text/richtext', - 'scm'=>'application/x-lotusscreencam', - 'set'=>'application/set', + 's'=>'text/x-asm', + 's3m'=>'audio/s3m', + 'saf'=>'application/vnd.yamaha.smaf-audio', + 'sbml'=>'application/sbml+xml', + 'sc'=>'application/vnd.ibm.secure-container', + 'scd'=>'application/x-msschedule', + 'scm'=>'application/vnd.lotus-screencam', + 'scq'=>'application/scvp-cv-request', + 'scs'=>'application/scvp-cv-response', + 'scurl'=>'text/vnd.curl.scurl', + 'sda'=>'application/vnd.stardivision.draw', + 'sdc'=>'application/vnd.stardivision.calc', + 'sdd'=>'application/vnd.stardivision.impress', + 'sdkd'=>'application/vnd.solent.sdkm+xml', + 'sdkm'=>'application/vnd.solent.sdkm+xml', + 'sdp'=>'application/sdp', + 'sdw'=>'application/vnd.stardivision.writer', + 'see'=>'application/vnd.seemail', + 'seed'=>'application/vnd.fdsn.seed', + 'sema'=>'application/vnd.sema', + 'semd'=>'application/vnd.semd', + 'semf'=>'application/vnd.semf', + 'ser'=>'application/java-serialized-object', + 'setpay'=>'application/set-payment-initiation', + 'setreg'=>'application/set-registration-initiation', + 'sfd-hdstx'=>'application/vnd.hydrostatix.sof-data', + 'sfs'=>'application/vnd.spotfire.sfs', + 'sfv'=>'text/x-sfv', + 'sgi'=>'image/sgi', + 'sgl'=>'application/vnd.stardivision.writer-global', 'sgm'=>'text/sgml', 'sgml'=>'text/sgml', 'sh'=>'application/x-sh', 'shar'=>'application/x-shar', + 'shf'=>'application/shf+xml', + 'sid'=>'image/x-mrsid-image', + 'sig'=>'application/pgp-signature', + 'sil'=>'audio/silk', 'silo'=>'model/mesh', + 'sis'=>'application/vnd.symbian.install', + 'sisx'=>'application/vnd.symbian.install', 'sit'=>'application/x-stuffit', - 'skd'=>'application/x-koan', - 'skm'=>'application/x-koan', - 'skp'=>'application/x-koan', - 'skt'=>'application/x-koan', - 'smi'=>'application/smil', - 'smil'=>'application/smil', + 'sitx'=>'application/x-stuffitx', + 'skd'=>'application/vnd.koan', + 'skm'=>'application/vnd.koan', + 'skp'=>'application/vnd.koan', + 'skt'=>'application/vnd.koan', + 'sldm'=>'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx'=>'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slt'=>'application/vnd.epson.salt', + 'sm'=>'application/vnd.stepmania.stepchart', + 'smf'=>'application/vnd.stardivision.math', + 'smi'=>'application/smil+xml', + 'smil'=>'application/smil+xml', + 'smv'=>'video/x-smv', + 'smzip'=>'application/vnd.stepmania.package', 'snd'=>'audio/basic', - 'sol'=>'application/solids', + 'snf'=>'application/x-font-snf', + 'so'=>'application/octet-stream', + 'spc'=>'application/x-pkcs7-certificates', + 'spf'=>'application/vnd.yamaha.smaf-phrase', 'spl'=>'application/x-futuresplash', + 'spot'=>'text/vnd.in3d.spot', + 'spp'=>'application/scvp-vp-response', + 'spq'=>'application/scvp-vp-request', 'spx'=>'audio/ogg', + 'sql'=>'application/x-sql', 'src'=>'application/x-wais-source', - 'step'=>'application/STEP', - 'stl'=>'application/SLA', - 'stp'=>'application/STEP', + 'srt'=>'application/x-subrip', + 'sru'=>'application/sru+xml', + 'srx'=>'application/sparql-results+xml', + 'ssdl'=>'application/ssdl+xml', + 'sse'=>'application/vnd.kodak-descriptor', + 'ssf'=>'application/vnd.epson.ssf', + 'ssml'=>'application/ssml+xml', + 'st'=>'application/vnd.sailingtracker.track', + 'stc'=>'application/vnd.sun.xml.calc.template', + 'std'=>'application/vnd.sun.xml.draw.template', + 'stf'=>'application/vnd.wt.stf', + 'sti'=>'application/vnd.sun.xml.impress.template', + 'stk'=>'application/hyperstudio', + 'stl'=>'application/vnd.ms-pki.stl', + 'str'=>'application/vnd.pg.format', + 'stw'=>'application/vnd.sun.xml.writer.template', + 'sub'=>'text/vnd.dvb.subtitle', + 'sus'=>'application/vnd.sus-calendar', + 'susp'=>'application/vnd.sus-calendar', 'sv4cpio'=>'application/x-sv4cpio', 'sv4crc'=>'application/x-sv4crc', + 'svc'=>'application/vnd.dvb.service', + 'svd'=>'application/vnd.svd', + 'svg'=>'image/svg+xml', + 'svgz'=>'image/svg+xml', + 'swa'=>'application/x-director', 'swf'=>'application/x-shockwave-flash', - 't'=>'application/x-troff', + 'swi'=>'application/vnd.aristanetworks.swi', + 'sxc'=>'application/vnd.sun.xml.calc', + 'sxd'=>'application/vnd.sun.xml.draw', + 'sxg'=>'application/vnd.sun.xml.writer.global', + 'sxi'=>'application/vnd.sun.xml.impress', + 'sxm'=>'application/vnd.sun.xml.math', + 'sxw'=>'application/vnd.sun.xml.writer', + 't'=>'text/troff', + 't3'=>'application/x-t3vm-image', + 'taglet'=>'application/vnd.mynfc', + 'tao'=>'application/vnd.tao.intent-module-archive', 'tar'=>'application/x-tar', + 'tcap'=>'application/vnd.3gpp2.tcap', 'tcl'=>'application/x-tcl', + 'teacher'=>'application/vnd.smart.teacher', + 'tei'=>'application/tei+xml', + 'teicorpus'=>'application/tei+xml', 'tex'=>'application/x-tex', 'texi'=>'application/x-texinfo', 'texinfo'=>'application/x-texinfo', + 'text'=>'text/plain', + 'tfi'=>'application/thraud+xml', + 'tfm'=>'application/x-tex-tfm', + 'tga'=>'image/x-tga', + 'thmx'=>'application/vnd.ms-officetheme', 'tif'=>'image/tiff', 'tiff'=>'image/tiff', - 'tr'=>'application/x-troff', - 'tsi'=>'audio/TSP-audio', - 'tsp'=>'application/dsptype', + 'tmo'=>'application/vnd.tmobile-livetv', + 'torrent'=>'application/x-bittorrent', + 'tpl'=>'application/vnd.groove-tool-template', + 'tpt'=>'application/vnd.trid.tpt', + 'tr'=>'text/troff', + 'tra'=>'application/vnd.trueapp', + 'trm'=>'application/x-msterminal', + 'tsd'=>'application/timestamped-data', 'tsv'=>'text/tab-separated-values', + 'ttc'=>'application/x-font-ttf', + 'ttf'=>'application/x-font-ttf', + 'ttl'=>'text/turtle', + 'twd'=>'application/vnd.simtech-mindmapper', + 'twds'=>'application/vnd.simtech-mindmapper', + 'txd'=>'application/vnd.genomatix.tuxedo', + 'txf'=>'application/vnd.mobius.txf', 'txt'=>'text/plain', - 'unv'=>'application/i-deas', + 'u32'=>'application/x-authorware-bin', + 'udeb'=>'application/x-debian-package', + 'ufd'=>'application/vnd.ufdl', + 'ufdl'=>'application/vnd.ufdl', + 'ulx'=>'application/x-glulx', + 'umj'=>'application/vnd.umajin', + 'unityweb'=>'application/vnd.unity', + 'uoml'=>'application/vnd.uoml+xml', + 'uri'=>'text/uri-list', + 'uris'=>'text/uri-list', + 'urls'=>'text/uri-list', 'ustar'=>'application/x-ustar', + 'utz'=>'application/vnd.uiq.theme', + 'uu'=>'text/x-uuencode', + 'uva'=>'audio/vnd.dece.audio', + 'uvd'=>'application/vnd.dece.data', + 'uvf'=>'application/vnd.dece.data', + 'uvg'=>'image/vnd.dece.graphic', + 'uvh'=>'video/vnd.dece.hd', + 'uvi'=>'image/vnd.dece.graphic', + 'uvm'=>'video/vnd.dece.mobile', + 'uvp'=>'video/vnd.dece.pd', + 'uvs'=>'video/vnd.dece.sd', + 'uvt'=>'application/vnd.dece.ttml+xml', + 'uvu'=>'video/vnd.uvvu.mp4', + 'uvv'=>'video/vnd.dece.video', + 'uvva'=>'audio/vnd.dece.audio', + 'uvvd'=>'application/vnd.dece.data', + 'uvvf'=>'application/vnd.dece.data', + 'uvvg'=>'image/vnd.dece.graphic', + 'uvvh'=>'video/vnd.dece.hd', + 'uvvi'=>'image/vnd.dece.graphic', + 'uvvm'=>'video/vnd.dece.mobile', + 'uvvp'=>'video/vnd.dece.pd', + 'uvvs'=>'video/vnd.dece.sd', + 'uvvt'=>'application/vnd.dece.ttml+xml', + 'uvvu'=>'video/vnd.uvvu.mp4', + 'uvvv'=>'video/vnd.dece.video', + 'uvvx'=>'application/vnd.dece.unspecified', + 'uvvz'=>'application/vnd.dece.zip', + 'uvx'=>'application/vnd.dece.unspecified', + 'uvz'=>'application/vnd.dece.zip', + 'vcard'=>'text/vcard', 'vcd'=>'application/x-cdlink', - 'vda'=>'application/vda', + 'vcf'=>'text/x-vcard', + 'vcg'=>'application/vnd.groove-vcard', + 'vcs'=>'text/x-vcalendar', + 'vcx'=>'application/vnd.vcx', + 'vis'=>'application/vnd.visionary', 'viv'=>'video/vnd.vivo', - 'vivo'=>'video/vnd.vivo', + 'vob'=>'video/x-ms-vob', + 'vor'=>'application/vnd.stardivision.writer', + 'vox'=>'application/x-authorware-bin', 'vrml'=>'model/vrml', + 'vsd'=>'application/vnd.visio', + 'vsf'=>'application/vnd.vsf', + 'vss'=>'application/vnd.visio', + 'vst'=>'application/vnd.visio', + 'vsw'=>'application/vnd.visio', + 'vtu'=>'model/vnd.vtu', + 'vxml'=>'application/voicexml+xml', + 'w3d'=>'application/x-director', + 'wad'=>'application/x-doom', 'wav'=>'audio/x-wav', + 'wax'=>'audio/x-ms-wax', + 'wbmp'=>'image/vnd.wap.wbmp', + 'wbs'=>'application/vnd.criticaltools.wbs+xml', + 'wbxml'=>'application/vnd.wap.wbxml', + 'wcm'=>'application/vnd.ms-works', + 'wdb'=>'application/vnd.ms-works', + 'wdp'=>'image/vnd.ms-photo', + 'weba'=>'audio/webm', + 'webm'=>'video/webm', + 'webp'=>'image/webp', + 'wg'=>'application/vnd.pmi.widget', + 'wgt'=>'application/widget', + 'wks'=>'application/vnd.ms-works', + 'wm'=>'video/x-ms-wm', + 'wma'=>'audio/x-ms-wma', + 'wmd'=>'application/x-ms-wmd', + 'wmf'=>'application/x-msmetafile', + 'wml'=>'text/vnd.wap.wml', + 'wmlc'=>'application/vnd.wap.wmlc', + 'wmls'=>'text/vnd.wap.wmlscript', + 'wmlsc'=>'application/vnd.wap.wmlscriptc', + 'wmv'=>'video/x-ms-wmv', + 'wmx'=>'video/x-ms-wmx', + 'wmz'=>'application/x-msmetafile', + 'woff'=>'application/font-woff', + 'wpd'=>'application/vnd.wordperfect', + 'wpl'=>'application/vnd.ms-wpl', + 'wps'=>'application/vnd.ms-works', + 'wqd'=>'application/vnd.wqd', + 'wri'=>'application/x-mswrite', 'wrl'=>'model/vrml', + 'wsdl'=>'application/wsdl+xml', + 'wspolicy'=>'application/wspolicy+xml', + 'wtb'=>'application/vnd.webturbo', + 'wvx'=>'video/x-ms-wvx', + 'x32'=>'application/x-authorware-bin', + 'x3d'=>'model/x3d+xml', + 'x3db'=>'model/x3d+binary', + 'x3dbz'=>'model/x3d+binary', + 'x3dv'=>'model/x3d+vrml', + 'x3dvz'=>'model/x3d+vrml', + 'x3dz'=>'model/x3d+xml', + 'xaml'=>'application/xaml+xml', + 'xap'=>'application/x-silverlight-app', + 'xar'=>'application/vnd.xara', + 'xbap'=>'application/x-ms-xbap', + 'xbd'=>'application/vnd.fujixerox.docuworks.binder', 'xbm'=>'image/x-xbitmap', + 'xdf'=>'application/xcap-diff+xml', + 'xdm'=>'application/vnd.syncml.dm+xml', + 'xdp'=>'application/vnd.adobe.xdp+xml', + 'xdssc'=>'application/dssc+xml', + 'xdw'=>'application/vnd.fujixerox.docuworks', + 'xenc'=>'application/xenc+xml', + 'xer'=>'application/patch-ops-error+xml', + 'xfdf'=>'application/vnd.adobe.xfdf', + 'xfdl'=>'application/vnd.xfdl', + 'xht'=>'application/xhtml+xml', + 'xhtml'=>'application/xhtml+xml', + 'xhvml'=>'application/xv+xml', + 'xif'=>'image/vnd.xiff', + 'xla'=>'application/vnd.ms-excel', + 'xlam'=>'application/vnd.ms-excel.addin.macroenabled.12', 'xlc'=>'application/vnd.ms-excel', - 'xll'=>'application/vnd.ms-excel', + 'xlf'=>'application/x-xliff+xml', 'xlm'=>'application/vnd.ms-excel', 'xls'=>'application/vnd.ms-excel', + 'xlsb'=>'application/vnd.ms-excel.sheet.binary.macroenabled.12', + 'xlsm'=>'application/vnd.ms-excel.sheet.macroenabled.12', + 'xlsx'=>'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt'=>'application/vnd.ms-excel', + 'xltm'=>'application/vnd.ms-excel.template.macroenabled.12', + 'xltx'=>'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'xlw'=>'application/vnd.ms-excel', + 'xm'=>'audio/xm', 'xml'=>'application/xml', + 'xo'=>'application/vnd.olpc-sugar', + 'xop'=>'application/xop+xml', + 'xpi'=>'application/x-xpinstall', + 'xpl'=>'application/xproc+xml', 'xpm'=>'image/x-xpixmap', + 'xpr'=>'application/vnd.is-xpr', + 'xps'=>'application/vnd.ms-xpsdocument', + 'xpw'=>'application/vnd.intercon.formnet', + 'xpx'=>'application/vnd.intercon.formnet', + 'xsl'=>'application/xml', + 'xslt'=>'application/xslt+xml', + 'xsm'=>'application/vnd.syncml+xml', 'xspf'=>'application/xspf+xml', + 'xul'=>'application/vnd.mozilla.xul+xml', + 'xvm'=>'application/xv+xml', + 'xvml'=>'application/xv+xml', 'xwd'=>'image/x-xwindowdump', - 'xyz'=>'chemical/x-pdb', + 'xyz'=>'chemical/x-xyz', + 'xz'=>'application/x-xz', + 'yang'=>'application/yang', + 'yin'=>'application/yin+xml', + 'z1'=>'application/x-zmachine', + 'z2'=>'application/x-zmachine', + 'z3'=>'application/x-zmachine', + 'z4'=>'application/x-zmachine', + 'z5'=>'application/x-zmachine', + 'z6'=>'application/x-zmachine', + 'z7'=>'application/x-zmachine', + 'z8'=>'application/x-zmachine', + 'zaz'=>'application/vnd.zzazz.deck+xml', 'zip'=>'application/zip', + 'zir'=>'application/vnd.zul', + 'zirz'=>'application/vnd.zul', + 'zmm'=>'application/vnd.handheld-entertainment+xml', + 123=>'application/vnd.lotus-1-2-3', ); diff --git a/framework/validators/CEmailValidator.php b/framework/validators/CEmailValidator.php index 62df8832e8f..b1b98bdfa6b 100644 --- a/framework/validators/CEmailValidator.php +++ b/framework/validators/CEmailValidator.php @@ -68,8 +68,7 @@ class CEmailValidator extends CValidator protected function validateAttribute($object,$attribute) { $value=$object->$attribute; - if($this->allowEmpty && $this->isEmpty($value)) - return; + if(!$this->validateValue($value)) { $message=$this->message!==null?$this->message:Yii::t('yii','{attribute} is not a valid email address.'); @@ -87,6 +86,9 @@ protected function validateAttribute($object,$attribute) */ public function validateValue($value) { + if($this->allowEmpty && $this->isEmpty($value)) + return true; + if(is_string($value) && $this->validateIDN) $value=$this->encodeIDN($value); // make sure string length is limited to avoid DOS attacks @@ -175,9 +177,7 @@ protected function checkMxPorts($domain) */ protected function mxSort($a, $b) { - if($a['pri']==$b['pri']) - return 0; - return ($a['pri']<$b['pri'])?-1:1; + return $a['pri']-$b['pri']; } /** diff --git a/framework/validators/CFileValidator.php b/framework/validators/CFileValidator.php index 950f8d314b8..bc30d4ff3e5 100644 --- a/framework/validators/CFileValidator.php +++ b/framework/validators/CFileValidator.php @@ -64,6 +64,8 @@ class CFileValidator extends CValidator /** * @var boolean whether the attribute requires a file to be uploaded or not. * Defaults to false, meaning a file is required to be uploaded. + * When no file is uploaded, the owner attribute is set to null to prevent + * setting arbitrary values. */ public $allowEmpty=false; /** @@ -130,12 +132,6 @@ class CFileValidator extends CValidator * limit. */ public $tooMany; - /** - * @var boolean whether attributes listed with this validator should be considered safe for massive assignment. - * For this validator it defaults to false. - * @since 1.1.12 - */ - public $safe=false; /** * Set the attribute and then validates using {@link validateFile}. @@ -145,9 +141,9 @@ class CFileValidator extends CValidator */ protected function validateAttribute($object, $attribute) { + $files=$object->$attribute; if($this->maxFiles > 1) { - $files=$object->$attribute; if(!is_array($files) || !isset($files[0]) || !$files[0] instanceof CUploadedFile) $files = CUploadedFile::getInstances($object, $attribute); if(array()===$files) @@ -163,7 +159,19 @@ protected function validateAttribute($object, $attribute) } else { - $file = $object->$attribute; + if (is_array($files)) + { + if (count($files) > 1) + { + $message=$this->tooMany!==null?$this->tooMany : Yii::t('yii', '{attribute} cannot accept more than {limit} files.'); + $this->addError($object, $attribute, $message, array('{attribute}'=>$attribute, '{limit}'=>$this->maxFiles)); + return; + } + else + $file = empty($files) ? null : reset($files); + } + else + $file = $files; if(!$file instanceof CUploadedFile) { $file = CUploadedFile::getInstance($object, $attribute); @@ -183,26 +191,34 @@ protected function validateAttribute($object, $attribute) */ protected function validateFile($object, $attribute, $file) { - if(null===$file || ($error=$file->getError())==UPLOAD_ERR_NO_FILE) - return $this->emptyAttribute($object, $attribute); - elseif($error==UPLOAD_ERR_INI_SIZE || $error==UPLOAD_ERR_FORM_SIZE || $this->maxSize!==null && $file->getSize()>$this->maxSize) + $error=(null===$file ? null : $file->getError()); + if($error==UPLOAD_ERR_INI_SIZE || $error==UPLOAD_ERR_FORM_SIZE || $this->maxSize!==null && $file->getSize()>$this->maxSize) { $message=$this->tooLarge!==null?$this->tooLarge : Yii::t('yii','The file "{file}" is too large. Its size cannot exceed {limit} bytes.'); - $this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{limit}'=>$this->getSizeLimit())); + $this->addError($object,$attribute,$message,array('{file}'=>CHtml::encode($file->getName()), '{limit}'=>$this->getSizeLimit())); + if($error!==UPLOAD_ERR_OK) + return; + } + elseif($error!==UPLOAD_ERR_OK) + { + if($error==UPLOAD_ERR_NO_FILE) + return $this->emptyAttribute($object, $attribute); + elseif($error==UPLOAD_ERR_PARTIAL) + throw new CException(Yii::t('yii','The file "{file}" was only partially uploaded.',array('{file}'=>CHtml::encode($file->getName())))); + elseif($error==UPLOAD_ERR_NO_TMP_DIR) + throw new CException(Yii::t('yii','Missing the temporary folder to store the uploaded file "{file}".',array('{file}'=>CHtml::encode($file->getName())))); + elseif($error==UPLOAD_ERR_CANT_WRITE) + throw new CException(Yii::t('yii','Failed to write the uploaded file "{file}" to disk.',array('{file}'=>CHtml::encode($file->getName())))); + elseif(defined('UPLOAD_ERR_EXTENSION') && $error==UPLOAD_ERR_EXTENSION) // available for PHP 5.2.0 or above + throw new CException(Yii::t('yii','A PHP extension stopped the file upload.')); + else + throw new CException(Yii::t('yii','Unable to upload the file "{file}" because of an unrecognized error.',array('{file}'=>CHtml::encode($file->getName())))); } - elseif($error==UPLOAD_ERR_PARTIAL) - throw new CException(Yii::t('yii','The file "{file}" was only partially uploaded.',array('{file}'=>$file->getName()))); - elseif($error==UPLOAD_ERR_NO_TMP_DIR) - throw new CException(Yii::t('yii','Missing the temporary folder to store the uploaded file "{file}".',array('{file}'=>$file->getName()))); - elseif($error==UPLOAD_ERR_CANT_WRITE) - throw new CException(Yii::t('yii','Failed to write the uploaded file "{file}" to disk.',array('{file}'=>$file->getName()))); - elseif(defined('UPLOAD_ERR_EXTENSION') && $error==UPLOAD_ERR_EXTENSION) // available for PHP 5.2.0 or above - throw new CException(Yii::t('yii','A PHP extension stopped the file upload.')); if($this->minSize!==null && $file->getSize()<$this->minSize) { $message=$this->tooSmall!==null?$this->tooSmall : Yii::t('yii','The file "{file}" is too small. Its size cannot be smaller than {limit} bytes.'); - $this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{limit}'=>$this->minSize)); + $this->addError($object,$attribute,$message,array('{file}'=>CHtml::encode($file->getName()), '{limit}'=>$this->minSize)); } if($this->types!==null) @@ -214,11 +230,11 @@ protected function validateFile($object, $attribute, $file) if(!in_array(strtolower($file->getExtensionName()),$types)) { $message=$this->wrongType!==null?$this->wrongType : Yii::t('yii','The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.'); - $this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{extensions}'=>implode(', ',$types))); + $this->addError($object,$attribute,$message,array('{file}'=>CHtml::encode($file->getName()), '{extensions}'=>implode(', ',$types))); } } - if($this->mimeTypes!==null) + if($this->mimeTypes!==null && !empty($file->tempName)) { if(function_exists('finfo_open')) { @@ -239,18 +255,22 @@ protected function validateFile($object, $attribute, $file) if($mimeType===false || !in_array(strtolower($mimeType),$mimeTypes)) { $message=$this->wrongMimeType!==null?$this->wrongMimeType : Yii::t('yii','The file "{file}" cannot be uploaded. Only files of these MIME-types are allowed: {mimeTypes}.'); - $this->addError($object,$attribute,$message,array('{file}'=>$file->getName(), '{mimeTypes}'=>implode(', ',$mimeTypes))); + $this->addError($object,$attribute,$message,array('{file}'=>CHtml::encode($file->getName()), '{mimeTypes}'=>implode(', ',$mimeTypes))); } } } /** * Raises an error to inform end user about blank attribute. + * Sets the owner attribute to null to prevent setting arbitrary values. * @param CModel $object the object being validated * @param string $attribute the attribute being validated */ protected function emptyAttribute($object, $attribute) { + if($this->safe) + $object->$attribute=null; + if(!$this->allowEmpty) { $message=$this->message!==null?$this->message : Yii::t('yii','{attribute} cannot be blank.'); @@ -302,4 +322,4 @@ public function sizeToBytes($sizeStr) default: return (int)$sizeStr; // do nothing } } -} \ No newline at end of file +} diff --git a/framework/validators/CRangeValidator.php b/framework/validators/CRangeValidator.php index e9123d403d0..aa72ad12aa8 100644 --- a/framework/validators/CRangeValidator.php +++ b/framework/validators/CRangeValidator.php @@ -59,7 +59,7 @@ protected function validateAttribute($object,$attribute) { foreach($this->range as $r) { - $result=(strcmp($r,$value)===0); + $result = $r === '' || $value === '' ? $r === $value : $r == $value; if($result) break; } diff --git a/framework/validators/CValidator.php b/framework/validators/CValidator.php index 84ae6431f38..edcd7efb1b2 100644 --- a/framework/validators/CValidator.php +++ b/framework/validators/CValidator.php @@ -13,18 +13,6 @@ * * Child classes must implement the {@link validateAttribute} method. * - * The following properties are defined in CValidator: - *
      - *
    • {@link attributes}: array, list of attributes to be validated;
    • - *
    • {@link message}: string, the customized error message. The message - * may contain placeholders that will be replaced with the actual content. - * For example, the "{attribute}" placeholder will be replaced with the label - * of the problematic attribute. Different validators may define additional - * placeholders.
    • - *
    • {@link on}: string, in which scenario should the validator be in effect. - * This is used to match the 'on' parameter supplied when calling {@link CModel::validate}.
    • - *
    - * * When using {@link createValidator} to create a validator, the following aliases * are recognized as the corresponding built-in validator classes: *
      @@ -141,7 +129,7 @@ abstract protected function validateAttribute($object,$attribute); public static function createValidator($name,$object,$attributes,$params=array()) { if(is_string($attributes)) - $attributes=preg_split('/[\s,]+/',$attributes,-1,PREG_SPLIT_NO_EMPTY); + $attributes=preg_split('/\s*,\s*/',$attributes,-1,PREG_SPLIT_NO_EMPTY); if(isset($params['on'])) { diff --git a/framework/vendors/README.html b/framework/vendors/README.html index 4162ec93112..a0e5fd5e470 100644 --- a/framework/vendors/README.html +++ b/framework/vendors/README.html @@ -19,12 +19,12 @@

      Third-Party Library List

      - jQuery 1.8.3 + jQuery 1.11.1 MIT most JavaScript-related functionalities - jQuery UI 1.9.2 + jQuery UI 1.11.2 MIT the widgets under zii/widgets/jui @@ -100,7 +100,7 @@

      Third-Party Library List

      Net_IDNA2 - Punycode encoding and decoding (0.1.1 (beta) was released on 2010-12-09) - LGPL + LGPL CUrlValidator, CEmailValidator diff --git a/framework/vendors/htmlpurifier/HTMLPurifier.standalone.php b/framework/vendors/htmlpurifier/HTMLPurifier.standalone.php index f440202b4b4..64dbf106965 100644 --- a/framework/vendors/htmlpurifier/HTMLPurifier.standalone.php +++ b/framework/vendors/htmlpurifier/HTMLPurifier.standalone.php @@ -7,7 +7,7 @@ * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS * FILE, changes will be overwritten the next time the script is run. * - * @version 4.5.0 + * @version 4.6.0 * * @warning * You must *not* include any other HTML Purifier files before this file, @@ -39,7 +39,7 @@ */ /* - HTML Purifier 4.5.0 - Standards Compliant HTML Filtering + HTML Purifier 4.6.0 - Standards Compliant HTML Filtering Copyright (C) 2006-2008 Edward Z. Yang This library is free software; you can redistribute it and/or @@ -74,66 +74,97 @@ class HTMLPurifier { - /** Version of HTML Purifier */ - public $version = '4.5.0'; + /** + * Version of HTML Purifier. + * @type string + */ + public $version = '4.6.0'; - /** Constant with version of HTML Purifier */ - const VERSION = '4.5.0'; + /** + * Constant with version of HTML Purifier. + */ + const VERSION = '4.6.0'; - /** Global configuration object */ + /** + * Global configuration object. + * @type HTMLPurifier_Config + */ public $config; - /** Array of extra HTMLPurifier_Filter objects to run on HTML, for backwards compatibility */ + /** + * Array of extra filter objects to run on HTML, + * for backwards compatibility. + * @type HTMLPurifier_Filter[] + */ private $filters = array(); - /** Single instance of HTML Purifier */ + /** + * Single instance of HTML Purifier. + * @type HTMLPurifier + */ private static $instance; - protected $strategy, $generator; + /** + * @type HTMLPurifier_Strategy_Core + */ + protected $strategy; + + /** + * @type HTMLPurifier_Generator + */ + protected $generator; /** - * Resultant HTMLPurifier_Context of last run purification. Is an array - * of contexts if the last called method was purifyArray(). + * Resultant context of last run purification. + * Is an array of contexts if the last called method was purifyArray(). + * @type HTMLPurifier_Context */ public $context; /** * Initializes the purifier. - * @param $config Optional HTMLPurifier_Config object for all instances of - * the purifier, if omitted, a default configuration is - * supplied (which can be overridden on a per-use basis). + * + * @param HTMLPurifier_Config $config Optional HTMLPurifier_Config object + * for all instances of the purifier, if omitted, a default + * configuration is supplied (which can be overridden on a + * per-use basis). * The parameter can also be any type that * HTMLPurifier_Config::create() supports. */ - public function __construct($config = null) { - + public function __construct($config = null) + { $this->config = HTMLPurifier_Config::create($config); - - $this->strategy = new HTMLPurifier_Strategy_Core(); - + $this->strategy = new HTMLPurifier_Strategy_Core(); } /** * Adds a filter to process the output. First come first serve - * @param $filter HTMLPurifier_Filter object + * + * @param HTMLPurifier_Filter $filter HTMLPurifier_Filter object */ - public function addFilter($filter) { - trigger_error('HTMLPurifier->addFilter() is deprecated, use configuration directives in the Filter namespace or Filter.Custom', E_USER_WARNING); + public function addFilter($filter) + { + trigger_error( + 'HTMLPurifier->addFilter() is deprecated, use configuration directives' . + ' in the Filter namespace or Filter.Custom', + E_USER_WARNING + ); $this->filters[] = $filter; } /** * Filters an HTML snippet/document to be XSS-free and standards-compliant. * - * @param $html String of HTML to purify - * @param $config HTMLPurifier_Config object for this operation, if omitted, - * defaults to the config object specified during this + * @param string $html String of HTML to purify + * @param HTMLPurifier_Config $config Config object for this operation, + * if omitted, defaults to the config object specified during this * object's construction. The parameter can also be any type * that HTMLPurifier_Config::create() supports. - * @return Purified HTML + * + * @return string Purified HTML */ - public function purify($html, $config = null) { - + public function purify($html, $config = null) + { // :TODO: make the config merge in, instead of replace $config = $config ? HTMLPurifier_Config::create($config) : $this->config; @@ -171,8 +202,12 @@ public function purify($html, $config = null) { unset($filter_flags['Custom']); $filters = array(); foreach ($filter_flags as $filter => $flag) { - if (!$flag) continue; - if (strpos($filter, '.') !== false) continue; + if (!$flag) { + continue; + } + if (strpos($filter, '.') !== false) { + continue; + } $class = "HTMLPurifier_Filter_$filter"; $filters[] = new $class; } @@ -195,9 +230,12 @@ public function purify($html, $config = null) { // list of un-purified tokens $lexer->tokenizeHTML( // un-purified HTML - $html, $config, $context + $html, + $config, + $context ), - $config, $context + $config, + $context ) ); @@ -212,11 +250,15 @@ public function purify($html, $config = null) { /** * Filters an array of HTML snippets - * @param $config Optional HTMLPurifier_Config object for this operation. + * + * @param string[] $array_of_html Array of html snippets + * @param HTMLPurifier_Config $config Optional config object for this operation. * See HTMLPurifier::purify() for more details. - * @return Array of purified HTML + * + * @return string[] Array of purified HTML */ - public function purifyArray($array_of_html, $config = null) { + public function purifyArray($array_of_html, $config = null) + { $context_array = array(); foreach ($array_of_html as $key => $html) { $array_of_html[$key] = $this->purify($html, $config); @@ -228,11 +270,16 @@ public function purifyArray($array_of_html, $config = null) { /** * Singleton for enforcing just one HTML Purifier in your system - * @param $prototype Optional prototype HTMLPurifier instance to - * overload singleton with, or HTMLPurifier_Config - * instance to configure the generated version with. + * + * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype + * HTMLPurifier instance to overload singleton with, + * or HTMLPurifier_Config instance to configure the + * generated version with. + * + * @return HTMLPurifier */ - public static function instance($prototype = null) { + public static function instance($prototype = null) + { if (!self::$instance || $prototype) { if ($prototype instanceof HTMLPurifier) { self::$instance = $prototype; @@ -246,18 +293,98 @@ public static function instance($prototype = null) { } /** + * Singleton for enforcing just one HTML Purifier in your system + * + * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype + * HTMLPurifier instance to overload singleton with, + * or HTMLPurifier_Config instance to configure the + * generated version with. + * + * @return HTMLPurifier * @note Backwards compatibility, see instance() */ - public static function getInstance($prototype = null) { + public static function getInstance($prototype = null) + { return HTMLPurifier::instance($prototype); } - } +/** + * Converts a stream of HTMLPurifier_Token into an HTMLPurifier_Node, + * and back again. + * + * @note This transformation is not an equivalence. We mutate the input + * token stream to make it so; see all [MUT] markers in code. + */ +class HTMLPurifier_Arborize +{ + public static function arborize($tokens, $config, $context) { + $definition = $config->getHTMLDefinition(); + $parent = new HTMLPurifier_Token_Start($definition->info_parent); + $stack = array($parent->toNode()); + foreach ($tokens as $token) { + $token->skip = null; // [MUT] + $token->carryover = null; // [MUT] + if ($token instanceof HTMLPurifier_Token_End) { + $token->start = null; // [MUT] + $r = array_pop($stack); + assert($r->name === $token->name); + assert(empty($token->attr)); + $r->endCol = $token->col; + $r->endLine = $token->line; + $r->endArmor = $token->armor; + continue; + } + $node = $token->toNode(); + $stack[count($stack)-1]->children[] = $node; + if ($token instanceof HTMLPurifier_Token_Start) { + $stack[] = $node; + } + } + assert(count($stack) == 1); + return $stack[0]; + } + + public static function flatten($node, $config, $context) { + $level = 0; + $nodes = array($level => new HTMLPurifier_Queue(array($node))); + $closingTokens = array(); + $tokens = array(); + do { + while (!$nodes[$level]->isEmpty()) { + $node = $nodes[$level]->shift(); // FIFO + list($start, $end) = $node->toTokenPair(); + if ($level > 0) { + $tokens[] = $start; + } + if ($end !== NULL) { + $closingTokens[$level][] = $end; + } + if ($node instanceof HTMLPurifier_Node_Element) { + $level++; + $nodes[$level] = new HTMLPurifier_Queue(); + foreach ($node->children as $childNode) { + $nodes[$level]->push($childNode); + } + } + } + $level--; + if ($level && isset($closingTokens[$level])) { + while ($token = array_pop($closingTokens[$level])) { + $tokens[] = $token; + } + } + } while ($level > 0); + return $tokens; + } +} + + + /** * Defines common attribute collections that modules reference */ @@ -266,7 +393,8 @@ class HTMLPurifier_AttrCollections { /** - * Associative array of attribute collections, indexed by name + * Associative array of attribute collections, indexed by name. + * @type array */ public $info = array(); @@ -274,10 +402,11 @@ class HTMLPurifier_AttrCollections * Performs all expansions on internal data for use by other inclusions * It also collects all attribute collection extensions from * modules - * @param $attr_types HTMLPurifier_AttrTypes instance - * @param $modules Hash array of HTMLPurifier_HTMLModule members + * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance + * @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members */ - public function __construct($attr_types, $modules) { + public function __construct($attr_types, $modules) + { // load extensions from the modules foreach ($modules as $module) { foreach ($module->attr_collections as $coll_i => $coll) { @@ -288,7 +417,9 @@ public function __construct($attr_types, $modules) { if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) { // merge in includes $this->info[$coll_i][$attr_i] = array_merge( - $this->info[$coll_i][$attr_i], $attr); + $this->info[$coll_i][$attr_i], + $attr + ); continue; } $this->info[$coll_i][$attr_i] = $attr; @@ -307,20 +438,29 @@ public function __construct($attr_types, $modules) { /** * Takes a reference to an attribute associative array and performs * all inclusions specified by the zero index. - * @param &$attr Reference to attribute array + * @param array &$attr Reference to attribute array */ - public function performInclusions(&$attr) { - if (!isset($attr[0])) return; + public function performInclusions(&$attr) + { + if (!isset($attr[0])) { + return; + } $merge = $attr[0]; $seen = array(); // recursion guard // loop through all the inclusions for ($i = 0; isset($merge[$i]); $i++) { - if (isset($seen[$merge[$i]])) continue; + if (isset($seen[$merge[$i]])) { + continue; + } $seen[$merge[$i]] = true; // foreach attribute of the inclusion, copy it over - if (!isset($this->info[$merge[$i]])) continue; + if (!isset($this->info[$merge[$i]])) { + continue; + } foreach ($this->info[$merge[$i]] as $key => $value) { - if (isset($attr[$key])) continue; // also catches more inclusions + if (isset($attr[$key])) { + continue; + } // also catches more inclusions $attr[$key] = $value; } if (isset($this->info[$merge[$i]][0])) { @@ -334,20 +474,24 @@ public function performInclusions(&$attr) { /** * Expands all string identifiers in an attribute array by replacing * them with the appropriate values inside HTMLPurifier_AttrTypes - * @param &$attr Reference to attribute array - * @param $attr_types HTMLPurifier_AttrTypes instance + * @param array &$attr Reference to attribute array + * @param HTMLPurifier_AttrTypes $attr_types HTMLPurifier_AttrTypes instance */ - public function expandIdentifiers(&$attr, $attr_types) { - + public function expandIdentifiers(&$attr, $attr_types) + { // because foreach will process new elements we add, make sure we // skip duplicates $processed = array(); foreach ($attr as $def_i => $def) { // skip inclusions - if ($def_i === 0) continue; + if ($def_i === 0) { + continue; + } - if (isset($processed[$def_i])) continue; + if (isset($processed[$def_i])) { + continue; + } // determine whether or not attribute is required if ($required = (strpos($def_i, '*') !== false)) { @@ -378,9 +522,7 @@ public function expandIdentifiers(&$attr, $attr_types) { unset($attr[$def_i]); } } - } - } @@ -401,23 +543,25 @@ abstract class HTMLPurifier_AttrDef { /** - * Tells us whether or not an HTML attribute is minimized. Has no - * meaning in other contexts. + * Tells us whether or not an HTML attribute is minimized. + * Has no meaning in other contexts. + * @type bool */ public $minimized = false; /** - * Tells us whether or not an HTML attribute is required. Has no - * meaning in other contexts + * Tells us whether or not an HTML attribute is required. + * Has no meaning in other contexts + * @type bool */ public $required = false; /** * Validates and cleans passed string according to a definition. * - * @param $string String to be validated and cleaned. - * @param $config Mandatory HTMLPurifier_Config object. - * @param $context Mandatory HTMLPurifier_AttrContext object. + * @param string $string String to be validated and cleaned. + * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object. + * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object. */ abstract public function validate($string, $config, $context); @@ -442,7 +586,8 @@ abstract public function validate($string, $config, $context); * parsing XML, thus, this behavior may still be correct. We * assume that newlines have been normalized. */ - public function parseCDATA($string) { + public function parseCDATA($string) + { $string = trim($string); $string = str_replace(array("\n", "\t", "\r"), ' ', $string); return $string; @@ -450,10 +595,11 @@ public function parseCDATA($string) { /** * Factory method for creating this class from a string. - * @param $string String construction info - * @return Created AttrDef object corresponding to $string + * @param string $string String construction info + * @return HTMLPurifier_AttrDef Created AttrDef object corresponding to $string */ - public function make($string) { + public function make($string) + { // default implementation, return a flyweight of this object. // If $string has an effect on the returned object (i.e. you // need to overload this method), it is best @@ -464,16 +610,20 @@ public function make($string) { /** * Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work * properly. THIS IS A HACK! + * @param string $string a CSS colour definition + * @return string */ - protected function mungeRgb($string) { + protected function mungeRgb($string) + { return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string); } /** - * Parses a possibly escaped CSS string and returns the "pure" + * Parses a possibly escaped CSS string and returns the "pure" * version of it. */ - protected function expandCSSEscape($string) { + protected function expandCSSEscape($string) + { // flexibly parse it $ret = ''; for ($i = 0, $c = strlen($string); $i < $c; $i++) { @@ -486,25 +636,32 @@ protected function expandCSSEscape($string) { if (ctype_xdigit($string[$i])) { $code = $string[$i]; for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) { - if (!ctype_xdigit($string[$i])) break; + if (!ctype_xdigit($string[$i])) { + break; + } $code .= $string[$i]; } // We have to be extremely careful when adding // new characters, to make sure we're not breaking // the encoding. $char = HTMLPurifier_Encoder::unichr(hexdec($code)); - if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue; + if (HTMLPurifier_Encoder::cleanUTF8($char) === '') { + continue; + } $ret .= $char; - if ($i < $c && trim($string[$i]) !== '') $i--; + if ($i < $c && trim($string[$i]) !== '') { + $i--; + } + continue; + } + if ($string[$i] === "\n") { continue; } - if ($string[$i] === "\n") continue; } $ret .= $string[$i]; } return $ret; } - } @@ -531,37 +688,41 @@ abstract class HTMLPurifier_AttrTransform /** * Abstract: makes changes to the attributes dependent on multiple values. * - * @param $attr Assoc array of attributes, usually from + * @param array $attr Assoc array of attributes, usually from * HTMLPurifier_Token_Tag::$attr - * @param $config Mandatory HTMLPurifier_Config object. - * @param $context Mandatory HTMLPurifier_Context object - * @returns Processed attribute array. + * @param HTMLPurifier_Config $config Mandatory HTMLPurifier_Config object. + * @param HTMLPurifier_Context $context Mandatory HTMLPurifier_Context object + * @return array Processed attribute array. */ abstract public function transform($attr, $config, $context); /** * Prepends CSS properties to the style attribute, creating the * attribute if it doesn't exist. - * @param $attr Attribute array to process (passed by reference) - * @param $css CSS to prepend + * @param array &$attr Attribute array to process (passed by reference) + * @param string $css CSS to prepend */ - public function prependCSS(&$attr, $css) { + public function prependCSS(&$attr, $css) + { $attr['style'] = isset($attr['style']) ? $attr['style'] : ''; $attr['style'] = $css . $attr['style']; } /** * Retrieves and removes an attribute - * @param $attr Attribute array to process (passed by reference) - * @param $key Key of attribute to confiscate + * @param array &$attr Attribute array to process (passed by reference) + * @param mixed $key Key of attribute to confiscate + * @return mixed */ - public function confiscateAttr(&$attr, $key) { - if (!isset($attr[$key])) return null; + public function confiscateAttr(&$attr, $key) + { + if (!isset($attr[$key])) { + return null; + } $value = $attr[$key]; unset($attr[$key]); return $value; } - } @@ -574,7 +735,8 @@ public function confiscateAttr(&$attr, $key) { class HTMLPurifier_AttrTypes { /** - * Lookup array of attribute string identifiers to concrete implementations + * Lookup array of attribute string identifiers to concrete implementations. + * @type HTMLPurifier_AttrDef[] */ protected $info = array(); @@ -582,7 +744,8 @@ class HTMLPurifier_AttrTypes * Constructs the info array, supplying default implementations for attribute * types. */ - public function __construct() { + public function __construct() + { // XXX This is kind of poor, since we don't actually /clone/ // instances; instead, we use the supplied make() attribute. So, // the underlying class must know how to deal with arguments. @@ -622,36 +785,39 @@ public function __construct() { $this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true); } - private static function makeEnum($in) { + private static function makeEnum($in) + { return new HTMLPurifier_AttrDef_Clone(new HTMLPurifier_AttrDef_Enum(explode(',', $in))); } /** * Retrieves a type - * @param $type String type name - * @return Object AttrDef for type + * @param string $type String type name + * @return HTMLPurifier_AttrDef Object AttrDef for type */ - public function get($type) { - + public function get($type) + { // determine if there is any extra info tacked on - if (strpos($type, '#') !== false) list($type, $string) = explode('#', $type, 2); - else $string = ''; + if (strpos($type, '#') !== false) { + list($type, $string) = explode('#', $type, 2); + } else { + $string = ''; + } if (!isset($this->info[$type])) { trigger_error('Cannot retrieve undefined attribute type ' . $type, E_USER_ERROR); return; } - return $this->info[$type]->make($string); - } /** * Sets a new implementation for a type - * @param $type String type name - * @param $impl Object AttrDef for type + * @param string $type String type name + * @param HTMLPurifier_AttrDef $impl Object AttrDef for type */ - public function set($type, $impl) { + public function set($type, $impl) + { $this->info[$type] = $impl; } } @@ -669,17 +835,14 @@ class HTMLPurifier_AttrValidator { /** - * Validates the attributes of a token, returning a modified token + * Validates the attributes of a token, mutating it as necessary. * that has valid tokens - * @param $token Reference to token to validate. We require a reference - * because the operation this class performs on the token are - * not atomic, so the context CurrentToken to be updated - * throughout - * @param $config Instance of HTMLPurifier_Config - * @param $context Instance of HTMLPurifier_Context + * @param HTMLPurifier_Token $token Token to validate. + * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config + * @param HTMLPurifier_Context $context Instance of HTMLPurifier_Context */ - public function validateToken(&$token, &$config, $context) { - + public function validateToken($token, $config, $context) + { $definition = $config->getHTMLDefinition(); $e =& $context->get('ErrorCollector', true); @@ -692,12 +855,15 @@ public function validateToken(&$token, &$config, $context) { // initialize CurrentToken if necessary $current_token =& $context->get('CurrentToken', true); - if (!$current_token) $context->register('CurrentToken', $token); + if (!$current_token) { + $context->register('CurrentToken', $token); + } - if ( - !$token instanceof HTMLPurifier_Token_Start && + if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty - ) return $token; + ) { + return; + } // create alias to global definition array, see also $defs // DEFINITION CALL @@ -711,7 +877,9 @@ public function validateToken(&$token, &$config, $context) { foreach ($definition->info_attr_transform_pre as $transform) { $attr = $transform->transform($o = $attr, $config, $context); if ($e) { - if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + if ($attr != $o) { + $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } } } @@ -720,7 +888,9 @@ public function validateToken(&$token, &$config, $context) { foreach ($definition->info[$token->name]->attr_transform_pre as $transform) { $attr = $transform->transform($o = $attr, $config, $context); if ($e) { - if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + if ($attr != $o) { + $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } } } @@ -737,7 +907,7 @@ public function validateToken(&$token, &$config, $context) { foreach ($attr as $attr_key => $value) { // call the definition - if ( isset($defs[$attr_key]) ) { + if (isset($defs[$attr_key])) { // there is a local definition defined if ($defs[$attr_key] === false) { // We've explicitly been told not to allow this element. @@ -749,15 +919,19 @@ public function validateToken(&$token, &$config, $context) { } else { // validate according to the element's definition $result = $defs[$attr_key]->validate( - $value, $config, $context - ); + $value, + $config, + $context + ); } - } elseif ( isset($d_defs[$attr_key]) ) { + } elseif (isset($d_defs[$attr_key])) { // there is a global definition defined, validate according // to the global definition $result = $d_defs[$attr_key]->validate( - $value, $config, $context - ); + $value, + $config, + $context + ); } else { // system never heard of the attribute? DELETE! $result = false; @@ -767,7 +941,9 @@ public function validateToken(&$token, &$config, $context) { if ($result === false || $result === null) { // this is a generic error message that should replaced // with more specific ones when possible - if ($e) $e->send(E_ERROR, 'AttrValidator: Attribute removed'); + if ($e) { + $e->send(E_ERROR, 'AttrValidator: Attribute removed'); + } // remove the attribute unset($attr[$attr_key]); @@ -797,7 +973,9 @@ public function validateToken(&$token, &$config, $context) { foreach ($definition->info_attr_transform_post as $transform) { $attr = $transform->transform($o = $attr, $config, $context); if ($e) { - if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + if ($attr != $o) { + $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } } } @@ -805,14 +983,18 @@ public function validateToken(&$token, &$config, $context) { foreach ($definition->info[$token->name]->attr_transform_post as $transform) { $attr = $transform->transform($o = $attr, $config, $context); if ($e) { - if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + if ($attr != $o) { + $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); + } } } $token->attr = $attr; // destroy CurrentToken if we made it ourselves - if (!$current_token) $context->destroy('CurrentToken'); + if (!$current_token) { + $context->destroy('CurrentToken'); + } } @@ -856,11 +1038,15 @@ class HTMLPurifier_Bootstrap /** * Autoload function for HTML Purifier - * @param $class Class to load + * @param string $class Class to load + * @return bool */ - public static function autoload($class) { + public static function autoload($class) + { $file = HTMLPurifier_Bootstrap::getPath($class); - if (!$file) return false; + if (!$file) { + return false; + } // Technically speaking, it should be ok and more efficient to // just do 'require', but Antonio Parraga reports that with // Zend extensions such as Zend debugger and APC, this invariant @@ -872,9 +1058,14 @@ public static function autoload($class) { /** * Returns the path for a specific class. + * @param string $class Class path to get + * @return string */ - public static function getPath($class) { - if (strncmp('HTMLPurifier', $class, 12) !== 0) return false; + public static function getPath($class) + { + if (strncmp('HTMLPurifier', $class, 12) !== 0) { + return false; + } // Custom implementations if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) { $code = str_replace('_', '-', substr($class, 22)); @@ -882,16 +1073,19 @@ public static function getPath($class) { } else { $file = str_replace('_', '/', $class) . '.php'; } - if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) return false; + if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) { + return false; + } return $file; } /** * "Pre-registers" our autoloader on the SPL stack. */ - public static function registerAutoload() { + public static function registerAutoload() + { $autoload = array('HTMLPurifier_Bootstrap', 'autoload'); - if ( ($funcs = spl_autoload_functions()) === false ) { + if (($funcs = spl_autoload_functions()) === false) { spl_autoload_register($autoload); } elseif (function_exists('spl_autoload_unregister')) { if (version_compare(PHP_VERSION, '5.3.0', '>=')) { @@ -907,27 +1101,30 @@ public static function registerAutoload() { // places where we need to error out $reflector = new ReflectionMethod($func[0], $func[1]); if (!$reflector->isStatic()) { - throw new Exception(' - HTML Purifier autoloader registrar is not compatible + throw new Exception( + 'HTML Purifier autoloader registrar is not compatible with non-static object methods due to PHP Bug #44144; Please do not use HTMLPurifier.autoload.php (or any file that includes this file); instead, place the code: spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\')) - after your own autoloaders. - '); + after your own autoloaders.' + ); } // Suprisingly, spl_autoload_register supports the // Class::staticMethod callback format, although call_user_func doesn't - if ($compat) $func = implode('::', $func); + if ($compat) { + $func = implode('::', $func); + } } spl_autoload_unregister($func); } spl_autoload_register($autoload); - foreach ($funcs as $func) spl_autoload_register($func); + foreach ($funcs as $func) { + spl_autoload_register($func); + } } } } - } @@ -943,6 +1140,7 @@ abstract class HTMLPurifier_Definition /** * Has setup() been called yet? + * @type bool */ public $setup = false; @@ -954,31 +1152,35 @@ abstract class HTMLPurifier_Definition * is used and any writes to the raw definition object are short * circuited. See enduser-customize.html for the high-level * picture. + * @type bool */ public $optimized = null; /** * What type of definition is it? + * @type string */ public $type; /** * Sets up the definition object into the final form, something * not done by the constructor - * @param $config HTMLPurifier_Config instance + * @param HTMLPurifier_Config $config */ abstract protected function doSetup($config); /** * Setup function that aborts if already setup - * @param $config HTMLPurifier_Config instance + * @param HTMLPurifier_Config $config */ - public function setup($config) { - if ($this->setup) return; + public function setup($config) + { + if ($this->setup) { + return; + } $this->setup = true; $this->doSetup($config); } - } @@ -996,35 +1198,59 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition /** * Assoc array of attribute name to definition object. + * @type HTMLPurifier_AttrDef[] */ public $info = array(); /** * Constructs the info array. The meat of this class. + * @param HTMLPurifier_Config $config */ - protected function doSetup($config) { - + protected function doSetup($config) + { $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum( - array('left', 'right', 'center', 'justify'), false); + array('left', 'right', 'center', 'justify'), + false + ); $border_style = - $this->info['border-bottom-style'] = - $this->info['border-right-style'] = - $this->info['border-left-style'] = - $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum( - array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', - 'groove', 'ridge', 'inset', 'outset'), false); + $this->info['border-bottom-style'] = + $this->info['border-right-style'] = + $this->info['border-left-style'] = + $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum( + array( + 'none', + 'hidden', + 'dotted', + 'dashed', + 'solid', + 'double', + 'groove', + 'ridge', + 'inset', + 'outset' + ), + false + ); $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style); $this->info['clear'] = new HTMLPurifier_AttrDef_Enum( - array('none', 'left', 'right', 'both'), false); + array('none', 'left', 'right', 'both'), + false + ); $this->info['float'] = new HTMLPurifier_AttrDef_Enum( - array('none', 'left', 'right'), false); + array('none', 'left', 'right'), + false + ); $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum( - array('normal', 'italic', 'oblique'), false); + array('normal', 'italic', 'oblique'), + false + ); $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum( - array('normal', 'small-caps'), false); + array('normal', 'small-caps'), + false + ); $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite( array( @@ -1034,16 +1260,31 @@ protected function doSetup($config) { ); $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum( - array('inside', 'outside'), false); + array('inside', 'outside'), + false + ); $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum( - array('disc', 'circle', 'square', 'decimal', 'lower-roman', - 'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false); + array( + 'disc', + 'circle', + 'square', + 'decimal', + 'lower-roman', + 'upper-roman', + 'lower-alpha', + 'upper-alpha', + 'none' + ), + false + ); $this->info['list-style-image'] = $uri_or_none; $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config); $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum( - array('capitalize', 'uppercase', 'lowercase', 'none'), false); + array('capitalize', 'uppercase', 'lowercase', 'none'), + false + ); $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color(); $this->info['background-image'] = $uri_or_none; @@ -1056,104 +1297,137 @@ protected function doSetup($config) { $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition(); $border_color = - $this->info['border-top-color'] = - $this->info['border-bottom-color'] = - $this->info['border-left-color'] = - $this->info['border-right-color'] = - $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('transparent')), - new HTMLPurifier_AttrDef_CSS_Color() - )); + $this->info['border-top-color'] = + $this->info['border-bottom-color'] = + $this->info['border-left-color'] = + $this->info['border-right-color'] = + $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('transparent')), + new HTMLPurifier_AttrDef_CSS_Color() + ) + ); $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config); $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color); $border_width = - $this->info['border-top-width'] = - $this->info['border-bottom-width'] = - $this->info['border-left-width'] = - $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')), - new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative - )); + $this->info['border-top-width'] = + $this->info['border-bottom-width'] = + $this->info['border-left-width'] = + $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')), + new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative + ) + ); $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width); - $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('normal')), - new HTMLPurifier_AttrDef_CSS_Length() - )); - - $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('normal')), - new HTMLPurifier_AttrDef_CSS_Length() - )); - - $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small', - 'small', 'medium', 'large', 'x-large', 'xx-large', - 'larger', 'smaller')), - new HTMLPurifier_AttrDef_CSS_Percentage(), - new HTMLPurifier_AttrDef_CSS_Length() - )); - - $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('normal')), - new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives - new HTMLPurifier_AttrDef_CSS_Length('0'), - new HTMLPurifier_AttrDef_CSS_Percentage(true) - )); + $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('normal')), + new HTMLPurifier_AttrDef_CSS_Length() + ) + ); + + $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('normal')), + new HTMLPurifier_AttrDef_CSS_Length() + ) + ); + + $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum( + array( + 'xx-small', + 'x-small', + 'small', + 'medium', + 'large', + 'x-large', + 'xx-large', + 'larger', + 'smaller' + ) + ), + new HTMLPurifier_AttrDef_CSS_Percentage(), + new HTMLPurifier_AttrDef_CSS_Length() + ) + ); + + $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum(array('normal')), + new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives + new HTMLPurifier_AttrDef_CSS_Length('0'), + new HTMLPurifier_AttrDef_CSS_Percentage(true) + ) + ); $margin = - $this->info['margin-top'] = - $this->info['margin-bottom'] = - $this->info['margin-left'] = - $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length(), - new HTMLPurifier_AttrDef_CSS_Percentage(), - new HTMLPurifier_AttrDef_Enum(array('auto')) - )); + $this->info['margin-top'] = + $this->info['margin-bottom'] = + $this->info['margin-left'] = + $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage(), + new HTMLPurifier_AttrDef_Enum(array('auto')) + ) + ); $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin); // non-negative $padding = - $this->info['padding-top'] = - $this->info['padding-bottom'] = - $this->info['padding-left'] = - $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length('0'), - new HTMLPurifier_AttrDef_CSS_Percentage(true) - )); + $this->info['padding-top'] = + $this->info['padding-bottom'] = + $this->info['padding-left'] = + $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length('0'), + new HTMLPurifier_AttrDef_CSS_Percentage(true) + ) + ); $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding); - $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length(), - new HTMLPurifier_AttrDef_CSS_Percentage() - )); + $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + ) + ); - $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length('0'), - new HTMLPurifier_AttrDef_CSS_Percentage(true), - new HTMLPurifier_AttrDef_Enum(array('auto')) - )); + $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length('0'), + new HTMLPurifier_AttrDef_CSS_Percentage(true), + new HTMLPurifier_AttrDef_Enum(array('auto')) + ) + ); $max = $config->get('CSS.MaxImgLength'); $this->info['width'] = $this->info['height'] = $max === null ? - $trusted_wh : - new HTMLPurifier_AttrDef_Switch('img', - // For img tags: - new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length('0', $max), - new HTMLPurifier_AttrDef_Enum(array('auto')) - )), - // For everyone else: - $trusted_wh - ); + $trusted_wh : + new HTMLPurifier_AttrDef_Switch( + 'img', + // For img tags: + new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length('0', $max), + new HTMLPurifier_AttrDef_Enum(array('auto')) + ) + ), + // For everyone else: + $trusted_wh + ); $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration(); @@ -1161,8 +1435,23 @@ protected function doSetup($config) { // this could use specialized code $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum( - array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', - '400', '500', '600', '700', '800', '900'), false); + array( + 'normal', + 'bold', + 'bolder', + 'lighter', + '100', + '200', + '300', + '400', + '500', + '600', + '700', + '800', + '900' + ), + false + ); // MUST be called after other font properties, as it references // a CSSDefinition object @@ -1175,27 +1464,44 @@ protected function doSetup($config) { $this->info['border-left'] = $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config); - $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array( - 'collapse', 'separate')); + $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum( + array('collapse', 'separate') + ); - $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array( - 'top', 'bottom')); + $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum( + array('top', 'bottom') + ); - $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array( - 'auto', 'fixed')); + $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum( + array('auto', 'fixed') + ); - $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super', - 'top', 'text-top', 'middle', 'bottom', 'text-bottom')), - new HTMLPurifier_AttrDef_CSS_Length(), - new HTMLPurifier_AttrDef_CSS_Percentage() - )); + $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Enum( + array( + 'baseline', + 'sub', + 'super', + 'top', + 'text-top', + 'middle', + 'bottom', + 'text-bottom' + ) + ), + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage() + ) + ); $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2); // These CSS properties don't work on many browsers, but we live // in THE FUTURE! - $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap', 'normal', 'pre', 'pre-wrap', 'pre-line')); + $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum( + array('nowrap', 'normal', 'pre', 'pre-wrap', 'pre-line') + ); if ($config->get('CSS.Proprietary')) { $this->doSetupProprietary($config); @@ -1218,76 +1524,119 @@ protected function doSetup($config) { $this->setupConfigStuff($config); } - protected function doSetupProprietary($config) { + /** + * @param HTMLPurifier_Config $config + */ + protected function doSetupProprietary($config) + { // Internet Explorer only scrollbar colors - $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); - $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color(); - $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); - $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color(); - $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color(); - $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color(); + $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); // technically not proprietary, but CSS3, and no one supports it - $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); - $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); - $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); + $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); + $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); + $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); // only opacity, for now $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter(); // more CSS3 $this->info['page-break-after'] = - $this->info['page-break-before'] = new HTMLPurifier_AttrDef_Enum(array('auto','always','avoid','left','right')); - $this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto','avoid')); + $this->info['page-break-before'] = new HTMLPurifier_AttrDef_Enum( + array( + 'auto', + 'always', + 'avoid', + 'left', + 'right' + ) + ); + $this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid')); } - protected function doSetupTricky($config) { - $this->info['display'] = new HTMLPurifier_AttrDef_Enum(array( - 'inline', 'block', 'list-item', 'run-in', 'compact', - 'marker', 'table', 'inline-block', 'inline-table', 'table-row-group', - 'table-header-group', 'table-footer-group', 'table-row', - 'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none' - )); - $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array( - 'visible', 'hidden', 'collapse' - )); + /** + * @param HTMLPurifier_Config $config + */ + protected function doSetupTricky($config) + { + $this->info['display'] = new HTMLPurifier_AttrDef_Enum( + array( + 'inline', + 'block', + 'list-item', + 'run-in', + 'compact', + 'marker', + 'table', + 'inline-block', + 'inline-table', + 'table-row-group', + 'table-header-group', + 'table-footer-group', + 'table-row', + 'table-column-group', + 'table-column', + 'table-cell', + 'table-caption', + 'none' + ) + ); + $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum( + array('visible', 'hidden', 'collapse') + ); $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll')); } - protected function doSetupTrusted($config) { - $this->info['position'] = new HTMLPurifier_AttrDef_Enum(array( - 'static', 'relative', 'absolute', 'fixed' - )); + /** + * @param HTMLPurifier_Config $config + */ + protected function doSetupTrusted($config) + { + $this->info['position'] = new HTMLPurifier_AttrDef_Enum( + array('static', 'relative', 'absolute', 'fixed') + ); $this->info['top'] = $this->info['left'] = $this->info['right'] = - $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length(), - new HTMLPurifier_AttrDef_CSS_Percentage(), - new HTMLPurifier_AttrDef_Enum(array('auto')), - )); - $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Integer(), - new HTMLPurifier_AttrDef_Enum(array('auto')), - )); + $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_CSS_Length(), + new HTMLPurifier_AttrDef_CSS_Percentage(), + new HTMLPurifier_AttrDef_Enum(array('auto')), + ) + ); + $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite( + array( + new HTMLPurifier_AttrDef_Integer(), + new HTMLPurifier_AttrDef_Enum(array('auto')), + ) + ); } /** * Performs extra config-based processing. Based off of * HTMLPurifier_HTMLDefinition. + * @param HTMLPurifier_Config $config * @todo Refactor duplicate elements into common class (probably using * composition, not inheritance). */ - protected function setupConfigStuff($config) { - + protected function setupConfigStuff($config) + { // setup allowed elements - $support = "(for information on implementing this, see the ". - "support forums) "; + $support = "(for information on implementing this, see the " . + "support forums) "; $allowed_properties = $config->get('CSS.AllowedProperties'); if ($allowed_properties !== null) { foreach ($this->info as $name => $d) { - if(!isset($allowed_properties[$name])) unset($this->info[$name]); + if (!isset($allowed_properties[$name])) { + unset($this->info[$name]); + } unset($allowed_properties[$name]); } // emit errors @@ -1306,7 +1655,6 @@ protected function setupConfigStuff($config) { } } } - } } @@ -1315,48 +1663,52 @@ protected function setupConfigStuff($config) { /** - * Defines allowed child nodes and validates tokens against it. + * Defines allowed child nodes and validates nodes against it. */ abstract class HTMLPurifier_ChildDef { /** * Type of child definition, usually right-most part of class name lowercase. * Used occasionally in terms of context. + * @type string */ public $type; /** - * Bool that indicates whether or not an empty array of children is okay + * Indicates whether or not an empty array of children is okay. * * This is necessary for redundant checking when changes affecting * a child node may cause a parent node to now be disallowed. + * @type bool */ public $allow_empty; /** - * Lookup array of all elements that this definition could possibly allow + * Lookup array of all elements that this definition could possibly allow. + * @type array */ public $elements = array(); /** * Get lookup of tag names that should not close this element automatically. * All other elements will do so. + * @param HTMLPurifier_Config $config HTMLPurifier_Config object + * @return array */ - public function getAllowedElements($config) { + public function getAllowedElements($config) + { return $this->elements; } /** * Validates nodes according to definition and returns modification. * - * @param $tokens_of_children Array of HTMLPurifier_Token - * @param $config HTMLPurifier_Config object - * @param $context HTMLPurifier_Context object - * @return bool true to leave nodes as is - * @return bool false to remove parent node - * @return array of replacement child tokens - */ - abstract public function validateChildren($tokens_of_children, $config, $context); + * @param HTMLPurifier_Node[] $children Array of HTMLPurifier_Node + * @param HTMLPurifier_Config $config HTMLPurifier_Config object + * @param HTMLPurifier_Context $context HTMLPurifier_Context object + * @return bool|array true to leave nodes as is, false to remove parent node, array of replacement children + */ + abstract public function validateChildren($children, $config, $context); } @@ -1382,78 +1734,92 @@ class HTMLPurifier_Config /** * HTML Purifier's version + * @type string */ - public $version = '4.5.0'; + public $version = '4.6.0'; /** - * Bool indicator whether or not to automatically finalize - * the object if a read operation is done + * Whether or not to automatically finalize + * the object if a read operation is done. + * @type bool */ public $autoFinalize = true; // protected member variables /** - * Namespace indexed array of serials for specific namespaces (see - * getSerial() for more info). + * Namespace indexed array of serials for specific namespaces. + * @see getSerial() for more info. + * @type string[] */ protected $serials = array(); /** - * Serial for entire configuration object + * Serial for entire configuration object. + * @type string */ protected $serial; /** - * Parser for variables + * Parser for variables. + * @type HTMLPurifier_VarParser_Flexible */ protected $parser = null; /** - * Reference HTMLPurifier_ConfigSchema for value checking + * Reference HTMLPurifier_ConfigSchema for value checking. + * @type HTMLPurifier_ConfigSchema * @note This is public for introspective purposes. Please don't * abuse! */ public $def; /** - * Indexed array of definitions + * Indexed array of definitions. + * @type HTMLPurifier_Definition[] */ protected $definitions; /** - * Bool indicator whether or not config is finalized + * Whether or not config is finalized. + * @type bool */ protected $finalized = false; /** * Property list containing configuration directives. + * @type array */ protected $plist; /** - * Whether or not a set is taking place due to an - * alias lookup. + * Whether or not a set is taking place due to an alias lookup. + * @type bool */ private $aliasMode; /** - * Set to false if you do not want line and file numbers in errors + * Set to false if you do not want line and file numbers in errors. * (useful when unit testing). This will also compress some errors * and exceptions. + * @type bool */ public $chatty = true; /** * Current lock; only gets to this namespace are allowed. + * @type string */ private $lock; /** - * @param $definition HTMLPurifier_ConfigSchema that defines what directives - * are allowed. + * Constructor + * @param HTMLPurifier_ConfigSchema $definition ConfigSchema that defines + * what directives are allowed. + * @param HTMLPurifier_PropertyList $parent */ - public function __construct($definition, $parent = null) { + public function __construct($definition, $parent = null) + { $parent = $parent ? $parent : $definition->defaultPlist; $this->plist = new HTMLPurifier_PropertyList($parent); $this->def = $definition; // keep a copy around for checking @@ -1466,10 +1832,11 @@ public function __construct($definition, $parent = null) { * object. Can be: a HTMLPurifier_Config() object, * an array of directives based on loadArray(), * or a string filename of an ini file. - * @param HTMLPurifier_ConfigSchema Schema object - * @return Configured HTMLPurifier_Config object + * @param HTMLPurifier_ConfigSchema $schema Schema object + * @return HTMLPurifier_Config Configured object */ - public static function create($config, $schema = null) { + public static function create($config, $schema = null) + { if ($config instanceof HTMLPurifier_Config) { // pass-through return $config; @@ -1479,57 +1846,79 @@ public static function create($config, $schema = null) { } else { $ret = new HTMLPurifier_Config($schema); } - if (is_string($config)) $ret->loadIni($config); - elseif (is_array($config)) $ret->loadArray($config); + if (is_string($config)) { + $ret->loadIni($config); + } elseif (is_array($config)) $ret->loadArray($config); return $ret; } /** * Creates a new config object that inherits from a previous one. - * @param HTMLPurifier_Config $config Configuration object to inherit - * from. + * @param HTMLPurifier_Config $config Configuration object to inherit from. * @return HTMLPurifier_Config object with $config as its parent. */ - public static function inherit(HTMLPurifier_Config $config) { + public static function inherit(HTMLPurifier_Config $config) + { return new HTMLPurifier_Config($config->def, $config->plist); } /** * Convenience constructor that creates a default configuration object. - * @return Default HTMLPurifier_Config object. + * @return HTMLPurifier_Config default object. */ - public static function createDefault() { + public static function createDefault() + { $definition = HTMLPurifier_ConfigSchema::instance(); $config = new HTMLPurifier_Config($definition); return $config; } /** - * Retreives a value from the configuration. - * @param $key String key + * Retrieves a value from the configuration. + * + * @param string $key String key + * @param mixed $a + * + * @return mixed */ - public function get($key, $a = null) { + public function get($key, $a = null) + { if ($a !== null) { - $this->triggerError("Using deprecated API: use \$config->get('$key.$a') instead", E_USER_WARNING); + $this->triggerError( + "Using deprecated API: use \$config->get('$key.$a') instead", + E_USER_WARNING + ); $key = "$key.$a"; } - if (!$this->finalized) $this->autoFinalize(); + if (!$this->finalized) { + $this->autoFinalize(); + } if (!isset($this->def->info[$key])) { // can't add % due to SimpleTest bug - $this->triggerError('Cannot retrieve value of undefined directive ' . htmlspecialchars($key), - E_USER_WARNING); + $this->triggerError( + 'Cannot retrieve value of undefined directive ' . htmlspecialchars($key), + E_USER_WARNING + ); return; } if (isset($this->def->info[$key]->isAlias)) { $d = $this->def->info[$key]; - $this->triggerError('Cannot get value from aliased directive, use real name ' . $d->key, - E_USER_ERROR); + $this->triggerError( + 'Cannot get value from aliased directive, use real name ' . $d->key, + E_USER_ERROR + ); return; } if ($this->lock) { list($ns) = explode('.', $key); if ($ns !== $this->lock) { - $this->triggerError('Cannot get value of namespace ' . $ns . ' when lock for ' . $this->lock . ' is active, this probably indicates a Definition setup method is accessing directives that are not within its namespace', E_USER_ERROR); + $this->triggerError( + 'Cannot get value of namespace ' . $ns . ' when lock for ' . + $this->lock . + ' is active, this probably indicates a Definition setup method ' . + 'is accessing directives that are not within its namespace', + E_USER_ERROR + ); return; } } @@ -1537,15 +1926,24 @@ public function get($key, $a = null) { } /** - * Retreives an array of directives to values from a given namespace - * @param $namespace String namespace + * Retrieves an array of directives to values from a given namespace + * + * @param string $namespace String namespace + * + * @return array */ - public function getBatch($namespace) { - if (!$this->finalized) $this->autoFinalize(); + public function getBatch($namespace) + { + if (!$this->finalized) { + $this->autoFinalize(); + } $full = $this->getAll(); if (!isset($full[$namespace])) { - $this->triggerError('Cannot retrieve undefined namespace ' . htmlspecialchars($namespace), - E_USER_WARNING); + $this->triggerError( + 'Cannot retrieve undefined namespace ' . + htmlspecialchars($namespace), + E_USER_WARNING + ); return; } return $full[$namespace]; @@ -1554,11 +1952,15 @@ public function getBatch($namespace) { /** * Returns a SHA-1 signature of a segment of the configuration object * that uniquely identifies that particular configuration + * + * @param string $namespace Namespace to get serial for + * + * @return string * @note Revision is handled specially and is removed from the batch * before processing! - * @param $namespace Namespace to get serial for */ - public function getBatchSerial($namespace) { + public function getBatchSerial($namespace) + { if (empty($this->serials[$namespace])) { $batch = $this->getBatch($namespace); unset($batch['DefinitionRev']); @@ -1570,8 +1972,11 @@ public function getBatchSerial($namespace) { /** * Returns a SHA-1 signature for the entire configuration object * that uniquely identifies that particular configuration + * + * @return string */ - public function getSerial() { + public function getSerial() + { if (empty($this->serial)) { $this->serial = sha1(serialize($this->getAll())); } @@ -1580,10 +1985,14 @@ public function getSerial() { /** * Retrieves all directives, organized by namespace + * * @warning This is a pretty inefficient function, avoid if you can */ - public function getAll() { - if (!$this->finalized) $this->autoFinalize(); + public function getAll() + { + if (!$this->finalized) { + $this->autoFinalize(); + } $ret = array(); foreach ($this->plist->squash() as $name => $value) { list($ns, $key) = explode('.', $name, 2); @@ -1594,10 +2003,13 @@ public function getAll() { /** * Sets a value to configuration. - * @param $key String key - * @param $value Mixed value + * + * @param string $key key + * @param mixed $value value + * @param mixed $a */ - public function set($key, $value, $a = null) { + public function set($key, $value, $a = null) + { if (strpos($key, '.') === false) { $namespace = $key; $directive = $value; @@ -1607,18 +2019,25 @@ public function set($key, $value, $a = null) { } else { list($namespace) = explode('.', $key); } - if ($this->isFinalized('Cannot set directive after finalization')) return; + if ($this->isFinalized('Cannot set directive after finalization')) { + return; + } if (!isset($this->def->info[$key])) { - $this->triggerError('Cannot set undefined directive ' . htmlspecialchars($key) . ' to value', - E_USER_WARNING); + $this->triggerError( + 'Cannot set undefined directive ' . htmlspecialchars($key) . ' to value', + E_USER_WARNING + ); return; } $def = $this->def->info[$key]; if (isset($def->isAlias)) { if ($this->aliasMode) { - $this->triggerError('Double-aliases not allowed, please fix '. - 'ConfigSchema bug with' . $key, E_USER_ERROR); + $this->triggerError( + 'Double-aliases not allowed, please fix '. + 'ConfigSchema bug with' . $key, + E_USER_ERROR + ); return; } $this->aliasMode = true; @@ -1642,7 +2061,11 @@ public function set($key, $value, $a = null) { try { $value = $this->parser->parse($value, $type, $allow_null); } catch (HTMLPurifier_VarParserException $e) { - $this->triggerError('Value for ' . $key . ' is of invalid type, should be ' . HTMLPurifier_VarParser::getTypeName($type), E_USER_WARNING); + $this->triggerError( + 'Value for ' . $key . ' is of invalid type, should be ' . + HTMLPurifier_VarParser::getTypeName($type), + E_USER_WARNING + ); return; } if (is_string($value) && is_object($def)) { @@ -1652,8 +2075,11 @@ public function set($key, $value, $a = null) { } // check to see if the value is allowed if (isset($def->allowed) && !isset($def->allowed[$value])) { - $this->triggerError('Value not supported, valid values are: ' . - $this->_listify($def->allowed), E_USER_WARNING); + $this->triggerError( + 'Value not supported, valid values are: ' . + $this->_listify($def->allowed), + E_USER_WARNING + ); return; } } @@ -1671,63 +2097,83 @@ public function set($key, $value, $a = null) { /** * Convenience function for error reporting + * + * @param array $lookup + * + * @return string */ - private function _listify($lookup) { + private function _listify($lookup) + { $list = array(); - foreach ($lookup as $name => $b) $list[] = $name; + foreach ($lookup as $name => $b) { + $list[] = $name; + } return implode(', ', $list); } /** * Retrieves object reference to the HTML definition. - * @param $raw Return a copy that has not been setup yet. Must be + * + * @param bool $raw Return a copy that has not been setup yet. Must be * called before it's been setup, otherwise won't work. - * @param $optimized If true, this method may return null, to + * @param bool $optimized If true, this method may return null, to * indicate that a cached version of the modified * definition object is available and no further edits * are necessary. Consider using * maybeGetRawHTMLDefinition, which is more explicitly * named, instead. + * + * @return HTMLPurifier_HTMLDefinition */ - public function getHTMLDefinition($raw = false, $optimized = false) { + public function getHTMLDefinition($raw = false, $optimized = false) + { return $this->getDefinition('HTML', $raw, $optimized); } /** * Retrieves object reference to the CSS definition - * @param $raw Return a copy that has not been setup yet. Must be + * + * @param bool $raw Return a copy that has not been setup yet. Must be * called before it's been setup, otherwise won't work. - * @param $optimized If true, this method may return null, to + * @param bool $optimized If true, this method may return null, to * indicate that a cached version of the modified * definition object is available and no further edits * are necessary. Consider using * maybeGetRawCSSDefinition, which is more explicitly * named, instead. + * + * @return HTMLPurifier_CSSDefinition */ - public function getCSSDefinition($raw = false, $optimized = false) { + public function getCSSDefinition($raw = false, $optimized = false) + { return $this->getDefinition('CSS', $raw, $optimized); } /** * Retrieves object reference to the URI definition - * @param $raw Return a copy that has not been setup yet. Must be + * + * @param bool $raw Return a copy that has not been setup yet. Must be * called before it's been setup, otherwise won't work. - * @param $optimized If true, this method may return null, to + * @param bool $optimized If true, this method may return null, to * indicate that a cached version of the modified * definition object is available and no further edits * are necessary. Consider using * maybeGetRawURIDefinition, which is more explicitly * named, instead. + * + * @return HTMLPurifier_URIDefinition */ - public function getURIDefinition($raw = false, $optimized = false) { + public function getURIDefinition($raw = false, $optimized = false) + { return $this->getDefinition('URI', $raw, $optimized); } /** * Retrieves a definition - * @param $type Type of definition: HTML, CSS, etc - * @param $raw Whether or not definition should be returned raw - * @param $optimized Only has an effect when $raw is true. Whether + * + * @param string $type Type of definition: HTML, CSS, etc + * @param bool $raw Whether or not definition should be returned raw + * @param bool $optimized Only has an effect when $raw is true. Whether * or not to return null if the result is already present in * the cache. This is off by default for backwards * compatibility reasons, but you need to do things this @@ -1735,12 +2181,18 @@ public function getURIDefinition($raw = false, $optimized = false) { * Check out enduser-customize.html for more details. * We probably won't ever change this default, as much as the * maybe semantics is the "right thing to do." + * + * @throws HTMLPurifier_Exception + * @return HTMLPurifier_Definition */ - public function getDefinition($type, $raw = false, $optimized = false) { + public function getDefinition($type, $raw = false, $optimized = false) + { if ($optimized && !$raw) { throw new HTMLPurifier_Exception("Cannot set optimized = true when raw = false"); } - if (!$this->finalized) $this->autoFinalize(); + if (!$this->finalized) { + $this->autoFinalize(); + } // temporarily suspend locks, so we can handle recursive definition calls $lock = $this->lock; $this->lock = null; @@ -1758,7 +2210,9 @@ public function getDefinition($type, $raw = false, $optimized = false) { return $def; } else { $def->setup($this); - if ($def->optimized) $cache->add($def, $this); + if ($def->optimized) { + $cache->add($def, $this); + } return $def; } } @@ -1787,23 +2241,36 @@ public function getDefinition($type, $raw = false, $optimized = false) { if ($optimized) { if (is_null($this->get($type . '.DefinitionID'))) { // fatally error out if definition ID not set - throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID"); + throw new HTMLPurifier_Exception( + "Cannot retrieve raw version without specifying %$type.DefinitionID" + ); } } if (!empty($this->definitions[$type])) { $def = $this->definitions[$type]; if ($def->setup && !$optimized) { - $extra = $this->chatty ? " (try moving this code block earlier in your initialization)" : ""; - throw new HTMLPurifier_Exception("Cannot retrieve raw definition after it has already been setup" . $extra); + $extra = $this->chatty ? + " (try moving this code block earlier in your initialization)" : + ""; + throw new HTMLPurifier_Exception( + "Cannot retrieve raw definition after it has already been setup" . + $extra + ); } if ($def->optimized === null) { $extra = $this->chatty ? " (try flushing your cache)" : ""; - throw new HTMLPurifier_Exception("Optimization status of definition is unknown" . $extra); + throw new HTMLPurifier_Exception( + "Optimization status of definition is unknown" . $extra + ); } if ($def->optimized !== $optimized) { $msg = $optimized ? "optimized" : "unoptimized"; - $extra = $this->chatty ? " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)" : ""; - throw new HTMLPurifier_Exception("Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra); + $extra = $this->chatty ? + " (this backtrace is for the first inconsistent call, which was for a $msg raw definition)" + : ""; + throw new HTMLPurifier_Exception( + "Inconsistent use of optimized and unoptimized raw definition retrievals" . $extra + ); } } // check if definition was in memory @@ -1836,9 +2303,22 @@ public function getDefinition($type, $raw = false, $optimized = false) { if (!$optimized) { if (!is_null($this->get($type . '.DefinitionID'))) { if ($this->chatty) { - $this->triggerError("Due to a documentation error in previous version of HTML Purifier, your definitions are not being cached. If this is OK, you can remove the %$type.DefinitionRev and %$type.DefinitionID declaration. Otherwise, modify your code to use maybeGetRawDefinition, and test if the returned value is null before making any edits (if it is null, that means that a cached version is available, and no raw operations are necessary). See Customize for more details", E_USER_WARNING); + $this->triggerError( + 'Due to a documentation error in previous version of HTML Purifier, your ' . + 'definitions are not being cached. If this is OK, you can remove the ' . + '%$type.DefinitionRev and %$type.DefinitionID declaration. Otherwise, ' . + 'modify your code to use maybeGetRawDefinition, and test if the returned ' . + 'value is null before making any edits (if it is null, that means that a ' . + 'cached version is available, and no raw operations are necessary). See ' . + '' . + 'Customize for more details', + E_USER_WARNING + ); } else { - $this->triggerError("Useless DefinitionID declaration", E_USER_WARNING); + $this->triggerError( + "Useless DefinitionID declaration", + E_USER_WARNING + ); } } } @@ -1850,7 +2330,16 @@ public function getDefinition($type, $raw = false, $optimized = false) { throw new HTMLPurifier_Exception("The impossible happened!"); } - private function initDefinition($type) { + /** + * Initialise definition + * + * @param string $type What type of definition to create + * + * @return HTMLPurifier_CSSDefinition|HTMLPurifier_HTMLDefinition|HTMLPurifier_URIDefinition + * @throws HTMLPurifier_Exception + */ + private function initDefinition($type) + { // quick checks failed, let's create the object if ($type == 'HTML') { $def = new HTMLPurifier_HTMLDefinition(); @@ -1859,35 +2348,45 @@ private function initDefinition($type) { } elseif ($type == 'URI') { $def = new HTMLPurifier_URIDefinition(); } else { - throw new HTMLPurifier_Exception("Definition of $type type not supported"); + throw new HTMLPurifier_Exception( + "Definition of $type type not supported" + ); } $this->definitions[$type] = $def; return $def; } - public function maybeGetRawDefinition($name) { + public function maybeGetRawDefinition($name) + { return $this->getDefinition($name, true, true); } - public function maybeGetRawHTMLDefinition() { + public function maybeGetRawHTMLDefinition() + { return $this->getDefinition('HTML', true, true); } - public function maybeGetRawCSSDefinition() { + public function maybeGetRawCSSDefinition() + { return $this->getDefinition('CSS', true, true); } - public function maybeGetRawURIDefinition() { + public function maybeGetRawURIDefinition() + { return $this->getDefinition('URI', true, true); } /** * Loads configuration values from an array with the following structure: * Namespace.Directive => Value - * @param $config_array Configuration associative array + * + * @param array $config_array Configuration associative array */ - public function loadArray($config_array) { - if ($this->isFinalized('Cannot load directives after finalization')) return; + public function loadArray($config_array) + { + if ($this->isFinalized('Cannot load directives after finalization')) { + return; + } foreach ($config_array as $key => $value) { $key = str_replace('_', '.', $key); if (strpos($key, '.') !== false) { @@ -1895,8 +2394,8 @@ public function loadArray($config_array) { } else { $namespace = $key; $namespace_values = $value; - foreach ($namespace_values as $directive => $value) { - $this->set($namespace .'.'. $directive, $value); + foreach ($namespace_values as $directive => $value2) { + $this->set($namespace .'.'. $directive, $value2); } } } @@ -1906,40 +2405,55 @@ public function loadArray($config_array) { * Returns a list of array(namespace, directive) for all directives * that are allowed in a web-form context as per an allowed * namespaces/directives list. - * @param $allowed List of allowed namespaces/directives + * + * @param array $allowed List of allowed namespaces/directives + * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy + * + * @return array */ - public static function getAllowedDirectivesForForm($allowed, $schema = null) { + public static function getAllowedDirectivesForForm($allowed, $schema = null) + { if (!$schema) { $schema = HTMLPurifier_ConfigSchema::instance(); } if ($allowed !== true) { - if (is_string($allowed)) $allowed = array($allowed); - $allowed_ns = array(); - $allowed_directives = array(); - $blacklisted_directives = array(); - foreach ($allowed as $ns_or_directive) { - if (strpos($ns_or_directive, '.') !== false) { - // directive - if ($ns_or_directive[0] == '-') { - $blacklisted_directives[substr($ns_or_directive, 1)] = true; - } else { - $allowed_directives[$ns_or_directive] = true; - } - } else { - // namespace - $allowed_ns[$ns_or_directive] = true; - } - } + if (is_string($allowed)) { + $allowed = array($allowed); + } + $allowed_ns = array(); + $allowed_directives = array(); + $blacklisted_directives = array(); + foreach ($allowed as $ns_or_directive) { + if (strpos($ns_or_directive, '.') !== false) { + // directive + if ($ns_or_directive[0] == '-') { + $blacklisted_directives[substr($ns_or_directive, 1)] = true; + } else { + $allowed_directives[$ns_or_directive] = true; + } + } else { + // namespace + $allowed_ns[$ns_or_directive] = true; + } + } } $ret = array(); foreach ($schema->info as $key => $def) { list($ns, $directive) = explode('.', $key, 2); if ($allowed !== true) { - if (isset($blacklisted_directives["$ns.$directive"])) continue; - if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue; + if (isset($blacklisted_directives["$ns.$directive"])) { + continue; + } + if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) { + continue; + } + } + if (isset($def->isAlias)) { + continue; + } + if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') { + continue; } - if (isset($def->isAlias)) continue; - if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue; $ret[] = array($ns, $directive); } return $ret; @@ -1948,13 +2462,17 @@ public static function getAllowedDirectivesForForm($allowed, $schema = null) { /** * Loads configuration values from $_GET/$_POST that were posted * via ConfigForm - * @param $array $_GET or $_POST array to import - * @param $index Index/name that the config variables are in - * @param $allowed List of allowed namespaces/directives - * @param $mq_fix Boolean whether or not to enable magic quotes fix - * @param $schema Instance of HTMLPurifier_ConfigSchema to use, if not global copy + * + * @param array $array $_GET or $_POST array to import + * @param string|bool $index Index/name that the config variables are in + * @param array|bool $allowed List of allowed namespaces/directives + * @param bool $mq_fix Boolean whether or not to enable magic quotes fix + * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy + * + * @return mixed */ - public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) { + public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) + { $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema); $config = HTMLPurifier_Config::create($ret, $schema); return $config; @@ -1962,9 +2480,14 @@ public static function loadArrayFromForm($array, $index = false, $allowed = true /** * Merges in configuration values from $_GET/$_POST to object. NOT STATIC. - * @note Same parameters as loadArrayFromForm + * + * @param array $array $_GET or $_POST array to import + * @param string|bool $index Index/name that the config variables are in + * @param array|bool $allowed List of allowed namespaces/directives + * @param bool $mq_fix Boolean whether or not to enable magic quotes fix */ - public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) { + public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) + { $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def); $this->loadArray($ret); } @@ -1972,9 +2495,20 @@ public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_ /** * Prepares an array from a form into something usable for the more * strict parts of HTMLPurifier_Config + * + * @param array $array $_GET or $_POST array to import + * @param string|bool $index Index/name that the config variables are in + * @param array|bool $allowed List of allowed namespaces/directives + * @param bool $mq_fix Boolean whether or not to enable magic quotes fix + * @param HTMLPurifier_ConfigSchema $schema Schema to use, if not global copy + * + * @return array */ - public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) { - if ($index !== false) $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array(); + public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) + { + if ($index !== false) { + $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array(); + } $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc(); $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema); @@ -1986,7 +2520,9 @@ public static function prepareArrayFromForm($array, $index = false, $allowed = t $ret[$ns][$directive] = null; continue; } - if (!isset($array[$skey])) continue; + if (!isset($array[$skey])) { + continue; + } $value = $mq ? stripslashes($array[$skey]) : $array[$skey]; $ret[$ns][$directive] = $value; } @@ -1995,19 +2531,27 @@ public static function prepareArrayFromForm($array, $index = false, $allowed = t /** * Loads configuration values from an ini file - * @param $filename Name of ini file + * + * @param string $filename Name of ini file */ - public function loadIni($filename) { - if ($this->isFinalized('Cannot load directives after finalization')) return; + public function loadIni($filename) + { + if ($this->isFinalized('Cannot load directives after finalization')) { + return; + } $array = parse_ini_file($filename, true); $this->loadArray($array); } /** * Checks whether or not the configuration object is finalized. - * @param $error String error message, or false for no error + * + * @param string|bool $error String error message, or false for no error + * + * @return bool */ - public function isFinalized($error = false) { + public function isFinalized($error = false) + { if ($this->finalized && $error) { $this->triggerError($error, E_USER_ERROR); } @@ -2018,7 +2562,8 @@ public function isFinalized($error = false) { * Finalizes configuration only if auto finalize is on and not * already finalized */ - public function autoFinalize() { + public function autoFinalize() + { if ($this->autoFinalize) { $this->finalize(); } else { @@ -2029,7 +2574,8 @@ public function autoFinalize() { /** * Finalizes a configuration object, prohibiting further change */ - public function finalize() { + public function finalize() + { $this->finalized = true; $this->parser = null; } @@ -2037,8 +2583,12 @@ public function finalize() { /** * Produces a nicely formatted error message by supplying the * stack frame information OUTSIDE of HTMLPurifier_Config. + * + * @param string $msg An error message + * @param int $no An error number */ - protected function triggerError($msg, $no) { + protected function triggerError($msg, $no) + { // determine previous stack frame $extra = ''; if ($this->chatty) { @@ -2060,8 +2610,11 @@ protected function triggerError($msg, $no) { /** * Returns a serialized form of the configuration object that can * be reconstituted. + * + * @return string */ - public function serialize() { + public function serialize() + { $this->getDefinition('HTML'); $this->getDefinition('CSS'); $this->getDefinition('URI'); @@ -2077,21 +2630,24 @@ public function serialize() { /** * Configuration definition, defines directives and their defaults. */ -class HTMLPurifier_ConfigSchema { - +class HTMLPurifier_ConfigSchema +{ /** * Defaults of the directives and namespaces. + * @type array * @note This shares the exact same structure as HTMLPurifier_Config::$conf */ public $defaults = array(); /** * The default property list. Do not edit this property list. + * @type array */ public $defaultPlist; /** - * Definition of the directives. The structure of this is: + * Definition of the directives. + * The structure of this is: * * array( * 'Namespace' => array( @@ -2118,22 +2674,27 @@ class HTMLPurifier_ConfigSchema { * This class is friendly with HTMLPurifier_Config. If you need introspection * about the schema, you're better of using the ConfigSchema_Interchange, * which uses more memory but has much richer information. + * @type array */ public $info = array(); /** * Application-wide singleton + * @type HTMLPurifier_ConfigSchema */ - static protected $singleton; + protected static $singleton; - public function __construct() { + public function __construct() + { $this->defaultPlist = new HTMLPurifier_PropertyList(); } /** * Unserializes the default ConfigSchema. + * @return HTMLPurifier_ConfigSchema */ - public static function makeFromSerial() { + public static function makeFromSerial() + { $contents = file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser'); $r = unserialize($contents); if (!$r) { @@ -2145,8 +2706,11 @@ public static function makeFromSerial() { /** * Retrieves an instance of the application-wide configuration definition. + * @param HTMLPurifier_ConfigSchema $prototype + * @return HTMLPurifier_ConfigSchema */ - public static function instance($prototype = null) { + public static function instance($prototype = null) + { if ($prototype !== null) { HTMLPurifier_ConfigSchema::$singleton = $prototype; } elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) { @@ -2160,17 +2724,19 @@ public static function instance($prototype = null) { * @warning Will fail of directive's namespace is defined. * @warning This method's signature is slightly different from the legacy * define() static method! Beware! - * @param $namespace Namespace the directive is in - * @param $name Key of directive - * @param $default Default value of directive - * @param $type Allowed type of the directive. See + * @param string $key Name of directive + * @param mixed $default Default value of directive + * @param string $type Allowed type of the directive. See * HTMLPurifier_DirectiveDef::$type for allowed values - * @param $allow_null Whether or not to allow null values + * @param bool $allow_null Whether or not to allow null values */ - public function add($key, $default, $type, $allow_null) { + public function add($key, $default, $type, $allow_null) + { $obj = new stdclass(); $obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type]; - if ($allow_null) $obj->allow_null = true; + if ($allow_null) { + $obj->allow_null = true; + } $this->info[$key] = $obj; $this->defaults[$key] = $default; $this->defaultPlist->set($key, $default); @@ -2181,11 +2747,11 @@ public function add($key, $default, $type, $allow_null) { * * Directive value aliases are convenient for developers because it lets * them set a directive to several values and get the same result. - * @param $namespace Directive's namespace - * @param $name Name of Directive - * @param $aliases Hash of aliased values to the real alias + * @param string $key Name of Directive + * @param array $aliases Hash of aliased values to the real alias */ - public function addValueAliases($key, $aliases) { + public function addValueAliases($key, $aliases) + { if (!isset($this->info[$key]->aliases)) { $this->info[$key]->aliases = array(); } @@ -2198,22 +2764,21 @@ public function addValueAliases($key, $aliases) { * Defines a set of allowed values for a directive. * @warning This is slightly different from the corresponding static * method definition. - * @param $namespace Namespace of directive - * @param $name Name of directive - * @param $allowed Lookup array of allowed values + * @param string $key Name of directive + * @param array $allowed Lookup array of allowed values */ - public function addAllowedValues($key, $allowed) { + public function addAllowedValues($key, $allowed) + { $this->info[$key]->allowed = $allowed; } /** * Defines a directive alias for backwards compatibility - * @param $namespace - * @param $name Directive that will be aliased - * @param $new_namespace - * @param $new_name Directive that the alias will be to + * @param string $key Directive that will be aliased + * @param string $new_key Directive that the alias will be to */ - public function addAlias($key, $new_key) { + public function addAlias($key, $new_key) + { $obj = new stdclass; $obj->key = $new_key; $obj->isAlias = true; @@ -2223,7 +2788,8 @@ public function addAlias($key, $new_key) { /** * Replaces any stdclass that only has the type property with type integer. */ - public function postProcess() { + public function postProcess() + { foreach ($this->info as $key => $v) { if (count((array) $v) == 1) { $this->info[$key] = $v->type; @@ -2232,7 +2798,6 @@ public function postProcess() { } } } - } @@ -2246,35 +2811,42 @@ class HTMLPurifier_ContentSets { /** - * List of content set strings (pipe seperators) indexed by name. + * List of content set strings (pipe separators) indexed by name. + * @type array */ public $info = array(); /** * List of content set lookups (element => true) indexed by name. + * @type array * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets */ public $lookup = array(); /** - * Synchronized list of defined content sets (keys of info) + * Synchronized list of defined content sets (keys of info). + * @type array */ protected $keys = array(); /** - * Synchronized list of defined content values (values of info) + * Synchronized list of defined content values (values of info). + * @type array */ protected $values = array(); /** * Merges in module's content sets, expands identifiers in the content * sets and populates the keys, values and lookup member variables. - * @param $modules List of HTMLPurifier_HTMLModule + * @param HTMLPurifier_HTMLModule[] $modules List of HTMLPurifier_HTMLModule */ - public function __construct($modules) { - if (!is_array($modules)) $modules = array($modules); + public function __construct($modules) + { + if (!is_array($modules)) { + $modules = array($modules); + } // populate content_sets based on module hints // sorry, no way of overloading - foreach ($modules as $module_i => $module) { + foreach ($modules as $module) { foreach ($module->content_sets as $key => $value) { $temp = $this->convertToLookup($value); if (isset($this->lookup[$key])) { @@ -2309,11 +2881,14 @@ public function __construct($modules) { /** * Accepts a definition; generates and assigns a ChildDef for it - * @param $def HTMLPurifier_ElementDef reference - * @param $module Module that defined the ElementDef + * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef reference + * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef */ - public function generateChildDef(&$def, $module) { - if (!empty($def->child)) return; // already done! + public function generateChildDef(&$def, $module) + { + if (!empty($def->child)) { // already done! + return; + } $content_model = $def->content_model; if (is_string($content_model)) { // Assume that $this->keys is alphanumeric @@ -2328,7 +2903,8 @@ public function generateChildDef(&$def, $module) { $def->child = $this->getChildDef($def, $module); } - public function generateChildDefCallback($matches) { + public function generateChildDefCallback($matches) + { return $this->info[$matches[0]]; } @@ -2337,10 +2913,12 @@ public function generateChildDefCallback($matches) { * member variables in HTMLPurifier_ElementDef * @note This will also defer to modules for custom HTMLPurifier_ChildDef * subclasses that need content set expansion - * @param $def HTMLPurifier_ElementDef to have ChildDef extracted + * @param HTMLPurifier_ElementDef $def HTMLPurifier_ElementDef to have ChildDef extracted + * @param HTMLPurifier_HTMLModule $module Module that defined the ElementDef * @return HTMLPurifier_ChildDef corresponding to ElementDef */ - public function getChildDef($def, $module) { + public function getChildDef($def, $module) + { $value = $def->content_model; if (is_object($value)) { trigger_error( @@ -2365,7 +2943,9 @@ public function getChildDef($def, $module) { if ($module->defines_child_def) { // save a func call $return = $module->getChildDef($def); } - if ($return !== false) return $return; + if ($return !== false) { + return $return; + } // error-out trigger_error( 'Could not determine which ChildDef class to instantiate', @@ -2377,18 +2957,18 @@ public function getChildDef($def, $module) { /** * Converts a string list of elements separated by pipes into * a lookup array. - * @param $string List of elements - * @return Lookup array of elements + * @param string $string List of elements + * @return array Lookup array of elements */ - protected function convertToLookup($string) { + protected function convertToLookup($string) + { $array = explode('|', str_replace(' ', '', $string)); $ret = array(); - foreach ($array as $i => $k) { + foreach ($array as $k) { $ret[$k] = true; } return $ret; } - } @@ -2407,18 +2987,22 @@ class HTMLPurifier_Context /** * Private array that stores the references. + * @type array */ private $_storage = array(); /** * Registers a variable into the context. - * @param $name String name - * @param $ref Reference to variable to be registered + * @param string $name String name + * @param mixed $ref Reference to variable to be registered */ - public function register($name, &$ref) { - if (isset($this->_storage[$name])) { - trigger_error("Name $name produces collision, cannot re-register", - E_USER_ERROR); + public function register($name, &$ref) + { + if (array_key_exists($name, $this->_storage)) { + trigger_error( + "Name $name produces collision, cannot re-register", + E_USER_ERROR + ); return; } $this->_storage[$name] =& $ref; @@ -2426,14 +3010,18 @@ public function register($name, &$ref) { /** * Retrieves a variable reference from the context. - * @param $name String name - * @param $ignore_error Boolean whether or not to ignore error + * @param string $name String name + * @param bool $ignore_error Boolean whether or not to ignore error + * @return mixed */ - public function &get($name, $ignore_error = false) { - if (!isset($this->_storage[$name])) { + public function &get($name, $ignore_error = false) + { + if (!array_key_exists($name, $this->_storage)) { if (!$ignore_error) { - trigger_error("Attempted to retrieve non-existent variable $name", - E_USER_ERROR); + trigger_error( + "Attempted to retrieve non-existent variable $name", + E_USER_ERROR + ); } $var = null; // so we can return by reference return $var; @@ -2442,13 +3030,16 @@ public function &get($name, $ignore_error = false) { } /** - * Destorys a variable in the context. - * @param $name String name + * Destroys a variable in the context. + * @param string $name String name */ - public function destroy($name) { - if (!isset($this->_storage[$name])) { - trigger_error("Attempted to destroy non-existent variable $name", - E_USER_ERROR); + public function destroy($name) + { + if (!array_key_exists($name, $this->_storage)) { + trigger_error( + "Attempted to destroy non-existent variable $name", + E_USER_ERROR + ); return; } unset($this->_storage[$name]); @@ -2456,22 +3047,24 @@ public function destroy($name) { /** * Checks whether or not the variable exists. - * @param $name String name + * @param string $name String name + * @return bool */ - public function exists($name) { - return isset($this->_storage[$name]); + public function exists($name) + { + return array_key_exists($name, $this->_storage); } /** * Loads a series of variables from an associative array - * @param $context_array Assoc array of variables to load + * @param array $context_array Assoc array of variables to load */ - public function loadArray($context_array) { + public function loadArray($context_array) + { foreach ($context_array as $key => $discard) { $this->register($key, $context_array[$key]); } } - } @@ -2488,22 +3081,27 @@ public function loadArray($context_array) { */ abstract class HTMLPurifier_DefinitionCache { - + /** + * @type string + */ public $type; /** - * @param $name Type of definition objects this instance of the + * @param string $type Type of definition objects this instance of the * cache will handle. */ - public function __construct($type) { + public function __construct($type) + { $this->type = $type; } /** * Generates a unique identifier for a particular configuration - * @param Instance of HTMLPurifier_Config + * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config + * @return string */ - public function generateKey($config) { + public function generateKey($config) + { return $config->version . ',' . // possibly replace with function calls $config->getBatchSerial($this->type) . ',' . $config->get($this->type . '.DefinitionRev'); @@ -2512,30 +3110,37 @@ public function generateKey($config) { /** * Tests whether or not a key is old with respect to the configuration's * version and revision number. - * @param $key Key to test - * @param $config Instance of HTMLPurifier_Config to test against + * @param string $key Key to test + * @param HTMLPurifier_Config $config Instance of HTMLPurifier_Config to test against + * @return bool */ - public function isOld($key, $config) { - if (substr_count($key, ',') < 2) return true; + public function isOld($key, $config) + { + if (substr_count($key, ',') < 2) { + return true; + } list($version, $hash, $revision) = explode(',', $key, 3); $compare = version_compare($version, $config->version); // version mismatch, is always old - if ($compare != 0) return true; + if ($compare != 0) { + return true; + } // versions match, ids match, check revision number - if ( - $hash == $config->getBatchSerial($this->type) && - $revision < $config->get($this->type . '.DefinitionRev') - ) return true; + if ($hash == $config->getBatchSerial($this->type) && + $revision < $config->get($this->type . '.DefinitionRev')) { + return true; + } return false; } /** * Checks if a definition's type jives with the cache's type * @note Throws an error on failure - * @param $def Definition object to check - * @return Boolean true if good, false if not + * @param HTMLPurifier_Definition $def Definition object to check + * @return bool true if good, false if not */ - public function checkDefType($def) { + public function checkDefType($def) + { if ($def->type !== $this->type) { trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}"); return false; @@ -2545,31 +3150,40 @@ public function checkDefType($def) { /** * Adds a definition object to the cache + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config */ abstract public function add($def, $config); /** * Unconditionally saves a definition object to the cache + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config */ abstract public function set($def, $config); /** * Replace an object in the cache + * @param HTMLPurifier_Definition $def + * @param HTMLPurifier_Config $config */ abstract public function replace($def, $config); /** * Retrieves a definition object from the cache + * @param HTMLPurifier_Config $config */ abstract public function get($config); /** * Removes a definition object to the cache + * @param HTMLPurifier_Config $config */ abstract public function remove($config); /** * Clears all objects from cache + * @param HTMLPurifier_Config $config */ abstract public function flush($config); @@ -2578,9 +3192,9 @@ abstract public function flush($config); * @note Be carefuly implementing this method as flush. Flush must * not interfere with other Definition types, and cleanup() * should not be repeatedly called by userland code. + * @param HTMLPurifier_Config $config */ abstract public function cleanup($config); - } @@ -2592,22 +3206,36 @@ abstract public function cleanup($config); */ class HTMLPurifier_DefinitionCacheFactory { - + /** + * @type array + */ protected $caches = array('Serializer' => array()); + + /** + * @type array + */ protected $implementations = array(); + + /** + * @type HTMLPurifier_DefinitionCache_Decorator[] + */ protected $decorators = array(); /** * Initialize default decorators */ - public function setup() { + public function setup() + { $this->addDecorator('Cleanup'); } /** * Retrieves an instance of global definition cache factory. + * @param HTMLPurifier_DefinitionCacheFactory $prototype + * @return HTMLPurifier_DefinitionCacheFactory */ - public static function instance($prototype = null) { + public static function instance($prototype = null) + { static $instance; if ($prototype !== null) { $instance = $prototype; @@ -2620,19 +3248,22 @@ public static function instance($prototype = null) { /** * Registers a new definition cache object - * @param $short Short name of cache object, for reference - * @param $long Full class name of cache object, for construction + * @param string $short Short name of cache object, for reference + * @param string $long Full class name of cache object, for construction */ - public function register($short, $long) { + public function register($short, $long) + { $this->implementations[$short] = $long; } /** * Factory method that creates a cache object based on configuration - * @param $name Name of definitions handled by cache - * @param $config Instance of HTMLPurifier_Config + * @param string $type Name of definitions handled by cache + * @param HTMLPurifier_Config $config Config instance + * @return mixed */ - public function create($type, $config) { + public function create($type, $config) + { $method = $config->get('Cache.DefinitionImpl'); if ($method === null) { return new HTMLPurifier_DefinitionCache_Null($type); @@ -2640,10 +3271,8 @@ public function create($type, $config) { if (!empty($this->caches[$method][$type])) { return $this->caches[$method][$type]; } - if ( - isset($this->implementations[$method]) && - class_exists($class = $this->implementations[$method], false) - ) { + if (isset($this->implementations[$method]) && + class_exists($class = $this->implementations[$method], false)) { $cache = new $class($type); } else { if ($method != 'Serializer') { @@ -2663,16 +3292,16 @@ class_exists($class = $this->implementations[$method], false) /** * Registers a decorator to add to all new cache objects - * @param + * @param HTMLPurifier_DefinitionCache_Decorator|string $decorator An instance or the name of a decorator */ - public function addDecorator($decorator) { + public function addDecorator($decorator) + { if (is_string($decorator)) { $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator"; $decorator = new $class; } $this->decorators[$decorator->name] = $decorator; } - } @@ -2689,42 +3318,55 @@ class HTMLPurifier_Doctype { /** * Full name of doctype + * @type string */ public $name; /** * List of standard modules (string identifiers or literal objects) * that this doctype uses + * @type array */ public $modules = array(); /** * List of modules to use for tidying up code + * @type array */ public $tidyModules = array(); /** * Is the language derived from XML (i.e. XHTML)? + * @type bool */ public $xml = true; /** * List of aliases for this doctype + * @type array */ public $aliases = array(); /** * Public DTD identifier + * @type string */ public $dtdPublic; /** * System DTD identifier + * @type string */ public $dtdSystem; - public function __construct($name = null, $xml = true, $modules = array(), - $tidyModules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null + public function __construct( + $name = null, + $xml = true, + $modules = array(), + $tidyModules = array(), + $aliases = array(), + $dtd_public = null, + $dtd_system = null ) { $this->name = $name; $this->xml = $xml; @@ -2744,12 +3386,14 @@ class HTMLPurifier_DoctypeRegistry { /** - * Hash of doctype names to doctype objects + * Hash of doctype names to doctype objects. + * @type array */ protected $doctypes; /** - * Lookup table of aliases to real doctype names + * Lookup table of aliases to real doctype names. + * @type array */ protected $aliases; @@ -2757,32 +3401,57 @@ class HTMLPurifier_DoctypeRegistry * Registers a doctype to the registry * @note Accepts a fully-formed doctype object, or the * parameters for constructing a doctype object - * @param $doctype Name of doctype or literal doctype object - * @param $modules Modules doctype will load - * @param $modules_for_modes Modules doctype will load for certain modes - * @param $aliases Alias names for doctype - * @return Editable registered doctype - */ - public function register($doctype, $xml = true, $modules = array(), - $tidy_modules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null + * @param string $doctype Name of doctype or literal doctype object + * @param bool $xml + * @param array $modules Modules doctype will load + * @param array $tidy_modules Modules doctype will load for certain modes + * @param array $aliases Alias names for doctype + * @param string $dtd_public + * @param string $dtd_system + * @return HTMLPurifier_Doctype Editable registered doctype + */ + public function register( + $doctype, + $xml = true, + $modules = array(), + $tidy_modules = array(), + $aliases = array(), + $dtd_public = null, + $dtd_system = null ) { - if (!is_array($modules)) $modules = array($modules); - if (!is_array($tidy_modules)) $tidy_modules = array($tidy_modules); - if (!is_array($aliases)) $aliases = array($aliases); + if (!is_array($modules)) { + $modules = array($modules); + } + if (!is_array($tidy_modules)) { + $tidy_modules = array($tidy_modules); + } + if (!is_array($aliases)) { + $aliases = array($aliases); + } if (!is_object($doctype)) { $doctype = new HTMLPurifier_Doctype( - $doctype, $xml, $modules, $tidy_modules, $aliases, $dtd_public, $dtd_system + $doctype, + $xml, + $modules, + $tidy_modules, + $aliases, + $dtd_public, + $dtd_system ); } $this->doctypes[$doctype->name] = $doctype; $name = $doctype->name; // hookup aliases foreach ($doctype->aliases as $alias) { - if (isset($this->doctypes[$alias])) continue; + if (isset($this->doctypes[$alias])) { + continue; + } $this->aliases[$alias] = $name; } // remove old aliases - if (isset($this->aliases[$name])) unset($this->aliases[$name]); + if (isset($this->aliases[$name])) { + unset($this->aliases[$name]); + } return $doctype; } @@ -2790,11 +3459,14 @@ public function register($doctype, $xml = true, $modules = array(), * Retrieves reference to a doctype of a certain name * @note This function resolves aliases * @note When possible, use the more fully-featured make() - * @param $doctype Name of doctype - * @return Editable doctype object + * @param string $doctype Name of doctype + * @return HTMLPurifier_Doctype Editable doctype object */ - public function get($doctype) { - if (isset($this->aliases[$doctype])) $doctype = $this->aliases[$doctype]; + public function get($doctype) + { + if (isset($this->aliases[$doctype])) { + $doctype = $this->aliases[$doctype]; + } if (!isset($this->doctypes[$doctype])) { trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR); $anon = new HTMLPurifier_Doctype($doctype); @@ -2810,20 +3482,30 @@ public function get($doctype) { * can hold on to (this is necessary in order to tell * Generator whether or not the current document is XML * based or not). + * @param HTMLPurifier_Config $config + * @return HTMLPurifier_Doctype */ - public function make($config) { + public function make($config) + { return clone $this->get($this->getDoctypeFromConfig($config)); } /** * Retrieves the doctype from the configuration object + * @param HTMLPurifier_Config $config + * @return string */ - public function getDoctypeFromConfig($config) { + public function getDoctypeFromConfig($config) + { // recommended test $doctype = $config->get('HTML.Doctype'); - if (!empty($doctype)) return $doctype; + if (!empty($doctype)) { + return $doctype; + } $doctype = $config->get('HTML.CustomDoctype'); - if (!empty($doctype)) return $doctype; + if (!empty($doctype)) { + return $doctype; + } // backwards-compatibility if ($config->get('HTML.XHTML')) { $doctype = 'XHTML 1.0'; @@ -2837,7 +3519,6 @@ public function getDoctypeFromConfig($config) { } return $doctype; } - } @@ -2854,15 +3535,16 @@ public function getDoctypeFromConfig($config) { */ class HTMLPurifier_ElementDef { - /** * Does the definition work by itself, or is it created solely * for the purpose of merging into another definition? + * @type bool */ public $standalone = true; /** - * Associative array of attribute name to HTMLPurifier_AttrDef + * Associative array of attribute name to HTMLPurifier_AttrDef. + * @type array * @note Before being processed by HTMLPurifier_AttrCollections * when modules are finalized during * HTMLPurifier_HTMLDefinition->setup(), this array may also @@ -2887,26 +3569,30 @@ class HTMLPurifier_ElementDef // nuking. /** - * List of tags HTMLPurifier_AttrTransform to be done before validation + * List of tags HTMLPurifier_AttrTransform to be done before validation. + * @type array */ public $attr_transform_pre = array(); /** - * List of tags HTMLPurifier_AttrTransform to be done after validation + * List of tags HTMLPurifier_AttrTransform to be done after validation. + * @type array */ public $attr_transform_post = array(); /** * HTMLPurifier_ChildDef of this tag. + * @type HTMLPurifier_ChildDef */ public $child; /** - * Abstract string representation of internal ChildDef rules. See - * HTMLPurifier_ContentSets for how this is parsed and then transformed + * Abstract string representation of internal ChildDef rules. + * @see HTMLPurifier_ContentSets for how this is parsed and then transformed * into an HTMLPurifier_ChildDef. * @warning This is a temporary variable that is not available after * being processed by HTMLDefinition + * @type string */ public $content_model; @@ -2916,27 +3602,29 @@ class HTMLPurifier_ElementDef * @warning This must be lowercase * @warning This is a temporary variable that is not available after * being processed by HTMLDefinition + * @type string */ public $content_model_type; - - /** * Does the element have a content model (#PCDATA | Inline)*? This * is important for chameleon ins and del processing in * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't * have to worry about this one. + * @type bool */ public $descendants_are_inline = false; /** - * List of the names of required attributes this element has. Dynamically - * populated by HTMLPurifier_HTMLDefinition::getElement + * List of the names of required attributes this element has. + * Dynamically populated by HTMLPurifier_HTMLDefinition::getElement() + * @type array */ public $required_attr = array(); /** * Lookup table of tags excluded from all descendants of this tag. + * @type array * @note SGML permits exclusions for all descendants, but this is * not possible with DTDs or XML Schemas. W3C has elected to * use complicated compositions of content_models to simulate @@ -2950,6 +3638,7 @@ class HTMLPurifier_ElementDef /** * This tag is explicitly auto-closed by the following tags. + * @type array */ public $autoclose = array(); @@ -2957,19 +3646,22 @@ class HTMLPurifier_ElementDef * If a foreign element is found in this element, test if it is * allowed by this sub-element; if it is, instead of closing the * current element, place it inside this element. + * @type string */ public $wrap; /** * Whether or not this is a formatting element affected by the * "Active Formatting Elements" algorithm. + * @type bool */ public $formatting; /** * Low-level factory constructor for creating new standalone element defs */ - public static function create($content_model, $content_model_type, $attr) { + public static function create($content_model, $content_model_type, $attr) + { $def = new HTMLPurifier_ElementDef(); $def->content_model = $content_model; $def->content_model_type = $content_model_type; @@ -2981,11 +3673,12 @@ public static function create($content_model, $content_model_type, $attr) { * Merges the values of another element definition into this one. * Values from the new element def take precedence if a value is * not mergeable. + * @param HTMLPurifier_ElementDef $def */ - public function mergeIn($def) { - + public function mergeIn($def) + { // later keys takes precedence - foreach($def->attr as $k => $v) { + foreach ($def->attr as $k => $v) { if ($k === 0) { // merge in the includes // sorry, no way to override an include @@ -2995,7 +3688,9 @@ public function mergeIn($def) { continue; } if ($v === false) { - if (isset($this->attr[$k])) unset($this->attr[$k]); + if (isset($this->attr[$k])) { + unset($this->attr[$k]); + } continue; } $this->attr[$k] = $v; @@ -3004,19 +3699,24 @@ public function mergeIn($def) { $this->attr_transform_pre = array_merge($this->attr_transform_pre, $def->attr_transform_pre); $this->attr_transform_post = array_merge($this->attr_transform_post, $def->attr_transform_post); - if(!empty($def->content_model)) { + if (!empty($def->content_model)) { $this->content_model = str_replace("#SUPER", $this->content_model, $def->content_model); $this->child = false; } - if(!empty($def->content_model_type)) { + if (!empty($def->content_model_type)) { $this->content_model_type = $def->content_model_type; $this->child = false; } - if(!is_null($def->child)) $this->child = $def->child; - if(!is_null($def->formatting)) $this->formatting = $def->formatting; - if($def->descendants_are_inline) $this->descendants_are_inline = $def->descendants_are_inline; - + if (!is_null($def->child)) { + $this->child = $def->child; + } + if (!is_null($def->formatting)) { + $this->formatting = $def->formatting; + } + if ($def->descendants_are_inline) { + $this->descendants_are_inline = $def->descendants_are_inline; + } } /** @@ -3024,16 +3724,18 @@ public function mergeIn($def) { * @param $a1 Array by reference that is merged into * @param $a2 Array that merges into $a1 */ - private function _mergeAssocArray(&$a1, $a2) { + private function _mergeAssocArray(&$a1, $a2) + { foreach ($a2 as $k => $v) { if ($v === false) { - if (isset($a1[$k])) unset($a1[$k]); + if (isset($a1[$k])) { + unset($a1[$k]); + } continue; } $a1[$k] = $v; } } - } @@ -3050,19 +3752,27 @@ class HTMLPurifier_Encoder /** * Constructor throws fatal error if you attempt to instantiate class */ - private function __construct() { + private function __construct() + { trigger_error('Cannot instantiate encoder, call methods statically', E_USER_ERROR); } /** * Error-handler that mutes errors, alternative to shut-up operator. */ - public static function muteErrorHandler() {} + public static function muteErrorHandler() + { + } /** * iconv wrapper which mutes errors, but doesn't work around bugs. + * @param string $in Input encoding + * @param string $out Output encoding + * @param string $text The text to convert + * @return string */ - public static function unsafeIconv($in, $out, $text) { + public static function unsafeIconv($in, $out, $text) + { set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler')); $r = iconv($in, $out, $text); restore_error_handler(); @@ -3071,8 +3781,14 @@ public static function unsafeIconv($in, $out, $text) { /** * iconv wrapper which mutes errors and works around bugs. - */ - public static function iconv($in, $out, $text, $max_chunk_size = 8000) { + * @param string $in Input encoding + * @param string $out Output encoding + * @param string $text The text to convert + * @param int $max_chunk_size + * @return string + */ + public static function iconv($in, $out, $text, $max_chunk_size = 8000) + { $code = self::testIconvTruncateBug(); if ($code == self::ICONV_OK) { return self::unsafeIconv($in, $out, $text); @@ -3127,6 +3843,10 @@ public static function iconv($in, $out, $text, $max_chunk_size = 8000) { * It will parse according to UTF-8 and return a valid UTF8 string, with * non-SGML codepoints excluded. * + * @param string $str The string to clean + * @param bool $force_php + * @return string + * * @note Just for reference, the non-SGML code points are 0 to 31 and * 127 to 159, inclusive. However, we allow code points 9, 10 * and 13, which are the tab, line feed and carriage return @@ -3146,14 +3866,17 @@ public static function iconv($in, $out, $text, $max_chunk_size = 8000) { * would need that, and I'm probably not going to implement them. * Once again, PHP 6 should solve all our problems. */ - public static function cleanUTF8($str, $force_php = false) { - + public static function cleanUTF8($str, $force_php = false) + { // UTF-8 validity is checked since PHP 4.3.5 // This is an optimization: if the string is already valid UTF-8, no // need to do PHP stuff. 99% of the time, this will be the case. // The regexp matches the XML char production, as well as well as excluding // non-SGML codepoints U+007F to U+009F - if (preg_match('/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du', $str)) { + if (preg_match( + '/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du', + $str + )) { return $str; } @@ -3172,7 +3895,7 @@ public static function cleanUTF8($str, $force_php = false) { $char = ''; $len = strlen($str); - for($i = 0; $i < $len; $i++) { + for ($i = 0; $i < $len; $i++) { $in = ord($str{$i}); $char .= $str[$i]; // append byte to char if (0 == $mState) { @@ -3325,8 +4048,9 @@ public static function cleanUTF8($str, $force_php = false) { // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes // +----------+----------+----------+----------+ - public static function unichr($code) { - if($code > 1114111 or $code < 0 or + public static function unichr($code) + { + if ($code > 1114111 or $code < 0 or ($code >= 55296 and $code <= 57343) ) { // bits are set outside the "valid" range as defined // by UNICODE 4.1.0 @@ -3344,7 +4068,7 @@ public static function unichr($code) { $y = (($code & 2047) >> 6) | 192; } else { $y = (($code & 4032) >> 6) | 128; - if($code < 65536) { + if ($code < 65536) { $z = (($code >> 12) & 15) | 224; } else { $z = (($code >> 12) & 63) | 128; @@ -3354,15 +4078,25 @@ public static function unichr($code) { } // set up the actual character $ret = ''; - if($w) $ret .= chr($w); - if($z) $ret .= chr($z); - if($y) $ret .= chr($y); + if ($w) { + $ret .= chr($w); + } + if ($z) { + $ret .= chr($z); + } + if ($y) { + $ret .= chr($y); + } $ret .= chr($x); return $ret; } - public static function iconvAvailable() { + /** + * @return bool + */ + public static function iconvAvailable() + { static $iconv = null; if ($iconv === null) { $iconv = function_exists('iconv') && self::testIconvTruncateBug() != self::ICONV_UNUSABLE; @@ -3371,13 +4105,22 @@ public static function iconvAvailable() { } /** - * Converts a string to UTF-8 based on configuration. + * Convert a string to UTF-8 based on configuration. + * @param string $str The string to convert + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string */ - public static function convertToUTF8($str, $config, $context) { + public static function convertToUTF8($str, $config, $context) + { $encoding = $config->get('Core.Encoding'); - if ($encoding === 'utf-8') return $str; + if ($encoding === 'utf-8') { + return $str; + } static $iconv = null; - if ($iconv === null) $iconv = self::iconvAvailable(); + if ($iconv === null) { + $iconv = self::iconvAvailable(); + } if ($iconv && !$config->get('Test.ForceNoIconv')) { // unaffected by bugs, since UTF-8 support all characters $str = self::unsafeIconv($encoding, 'utf-8//IGNORE', $str); @@ -3399,29 +4142,44 @@ public static function convertToUTF8($str, $config, $context) { if ($bug == self::ICONV_OK) { trigger_error('Encoding not supported, please install iconv', E_USER_ERROR); } else { - trigger_error('You have a buggy version of iconv, see https://bugs.php.net/bug.php?id=48147 and http://sourceware.org/bugzilla/show_bug.cgi?id=13541', E_USER_ERROR); + trigger_error( + 'You have a buggy version of iconv, see https://bugs.php.net/bug.php?id=48147 ' . + 'and http://sourceware.org/bugzilla/show_bug.cgi?id=13541', + E_USER_ERROR + ); } } /** * Converts a string from UTF-8 based on configuration. + * @param string $str The string to convert + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string * @note Currently, this is a lossy conversion, with unexpressable * characters being omitted. */ - public static function convertFromUTF8($str, $config, $context) { + public static function convertFromUTF8($str, $config, $context) + { $encoding = $config->get('Core.Encoding'); if ($escape = $config->get('Core.EscapeNonASCIICharacters')) { $str = self::convertToASCIIDumbLossless($str); } - if ($encoding === 'utf-8') return $str; + if ($encoding === 'utf-8') { + return $str; + } static $iconv = null; - if ($iconv === null) $iconv = self::iconvAvailable(); + if ($iconv === null) { + $iconv = self::iconvAvailable(); + } if ($iconv && !$config->get('Test.ForceNoIconv')) { // Undo our previous fix in convertToUTF8, otherwise iconv will barf $ascii_fix = self::testEncodingSupportsASCII($encoding); if (!$escape && !empty($ascii_fix)) { $clear_fix = array(); - foreach ($ascii_fix as $utf8 => $native) $clear_fix[$utf8] = ''; + foreach ($ascii_fix as $utf8 => $native) { + $clear_fix[$utf8] = ''; + } $str = strtr($str, $clear_fix); } $str = strtr($str, array_flip($ascii_fix)); @@ -3441,8 +4199,8 @@ public static function convertFromUTF8($str, $config, $context) { /** * Lossless (character-wise) conversion of HTML to ASCII - * @param $str UTF-8 string to be converted to ASCII - * @returns ASCII encoded string with non-ASCII character entity-ized + * @param string $str UTF-8 string to be converted to ASCII + * @return string ASCII encoded string with non-ASCII character entity-ized * @warning Adapted from MediaWiki, claiming fair use: this is a common * algorithm. If you disagree with this license fudgery, * implement it yourself. @@ -3455,27 +4213,28 @@ public static function convertFromUTF8($str, $config, $context) { * @note Sort of with cleanUTF8() but it assumes that $str is * well-formed UTF-8 */ - public static function convertToASCIIDumbLossless($str) { + public static function convertToASCIIDumbLossless($str) + { $bytesleft = 0; $result = ''; $working = 0; $len = strlen($str); - for( $i = 0; $i < $len; $i++ ) { - $bytevalue = ord( $str[$i] ); - if( $bytevalue <= 0x7F ) { //0xxx xxxx - $result .= chr( $bytevalue ); + for ($i = 0; $i < $len; $i++) { + $bytevalue = ord($str[$i]); + if ($bytevalue <= 0x7F) { //0xxx xxxx + $result .= chr($bytevalue); $bytesleft = 0; - } elseif( $bytevalue <= 0xBF ) { //10xx xxxx + } elseif ($bytevalue <= 0xBF) { //10xx xxxx $working = $working << 6; $working += ($bytevalue & 0x3F); $bytesleft--; - if( $bytesleft <= 0 ) { + if ($bytesleft <= 0) { $result .= "&#" . $working . ";"; } - } elseif( $bytevalue <= 0xDF ) { //110x xxxx + } elseif ($bytevalue <= 0xDF) { //110x xxxx $working = $bytevalue & 0x1F; $bytesleft = 1; - } elseif( $bytevalue <= 0xEF ) { //1110 xxxx + } elseif ($bytevalue <= 0xEF) { //1110 xxxx $working = $bytevalue & 0x0F; $bytesleft = 2; } else { //1111 0xxx @@ -3509,9 +4268,10 @@ public static function convertToASCIIDumbLossless($str) { * characters, as long as PHP ignores the error code. If PHP starts * paying attention to the error code, iconv becomes unusable. * - * @returns Error code indicating severity of bug. + * @return int Error code indicating severity of bug. */ - public static function testIconvTruncateBug() { + public static function testIconvTruncateBug() + { static $code = null; if ($code === null) { // better not use iconv, otherwise infinite loop! @@ -3521,7 +4281,11 @@ public static function testIconvTruncateBug() { } elseif (($c = strlen($r)) < 9000) { $code = self::ICONV_TRUNCATES; } elseif ($c > 9000) { - trigger_error('Your copy of iconv is extremely buggy. Please notify HTML Purifier maintainers: include your iconv version as per phpversion()', E_USER_ERROR); + trigger_error( + 'Your copy of iconv is extremely buggy. Please notify HTML Purifier maintainers: ' . + 'include your iconv version as per phpversion()', + E_USER_ERROR + ); } else { $code = self::ICONV_OK; } @@ -3540,7 +4304,8 @@ public static function testIconvTruncateBug() { * @return Array of UTF-8 characters to their corresponding ASCII, * which can be used to "undo" any overzealous iconv action. */ - public static function testEncodingSupportsASCII($encoding, $bypass = false) { + public static function testEncodingSupportsASCII($encoding, $bypass = false) + { // All calls to iconv here are unsafe, proof by case analysis: // If ICONV_OK, no difference. // If ICONV_TRUNCATE, all calls involve one character inputs, @@ -3548,7 +4313,9 @@ public static function testEncodingSupportsASCII($encoding, $bypass = false) { // If ICONV_UNUSABLE, this call is irrelevant static $encodings = array(); if (!$bypass) { - if (isset($encodings[$encoding])) return $encodings[$encoding]; + if (isset($encodings[$encoding])) { + return $encodings[$encoding]; + } $lenc = strtolower($encoding); switch ($lenc) { case 'shift_jis': @@ -3556,15 +4323,18 @@ public static function testEncodingSupportsASCII($encoding, $bypass = false) { case 'johab': return array("\xE2\x82\xA9" => '\\'); } - if (strpos($lenc, 'iso-8859-') === 0) return array(); + if (strpos($lenc, 'iso-8859-') === 0) { + return array(); + } } $ret = array(); - if (self::unsafeIconv('UTF-8', $encoding, 'a') === false) return false; + if (self::unsafeIconv('UTF-8', $encoding, 'a') === false) { + return false; + } for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars $c = chr($i); // UTF-8 char $r = self::unsafeIconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion - if ( - $r === '' || + if ($r === '' || // This line is needed for iconv implementations that do not // omit characters that do not exist in the target character set ($r === $c && self::unsafeIconv($encoding, 'UTF-8//IGNORE', $r) !== $c) @@ -3578,8 +4348,6 @@ public static function testEncodingSupportsASCII($encoding, $bypass = false) { $encodings[$encoding] = $ret; return $ret; } - - } @@ -3589,20 +4357,23 @@ public static function testEncodingSupportsASCII($encoding, $bypass = false) { /** * Object that provides entity lookup table from entity name to character */ -class HTMLPurifier_EntityLookup { - +class HTMLPurifier_EntityLookup +{ /** * Assoc array of entity name to character represented. + * @type array */ public $table; /** * Sets up the entity lookup table from the serialized file contents. + * @param bool $file * @note The serialized contents are versioned, but were generated * using the maintenance script generate_entity_file.php * @warning This is not in constructor to help enforce the Singleton */ - public function setup($file = false) { + public function setup($file = false) + { if (!$file) { $file = HTMLPURIFIER_PREFIX . '/HTMLPurifier/EntityLookup/entities.ser'; } @@ -3611,9 +4382,11 @@ public function setup($file = false) { /** * Retrieves sole instance of the object. - * @param Optional prototype of custom lookup table to overload with. + * @param bool|HTMLPurifier_EntityLookup $prototype Optional prototype of custom lookup table to overload with. + * @return HTMLPurifier_EntityLookup */ - public static function instance($prototype = false) { + public static function instance($prototype = false) + { // no references, since PHP doesn't copy unless modified static $instance = null; if ($prototype) { @@ -3624,7 +4397,6 @@ public static function instance($prototype = false) { } return $instance; } - } @@ -3643,19 +4415,21 @@ class HTMLPurifier_EntityParser /** * Reference to entity lookup table. + * @type HTMLPurifier_EntityLookup */ protected $_entity_lookup; /** * Callback regex string for parsing entities. + * @type string */ protected $_substituteEntitiesRegex = -'/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/'; -// 1. hex 2. dec 3. string (XML style) - + '/&(?:[#]x([a-fA-F0-9]+)|[#]0*(\d+)|([A-Za-z_:][A-Za-z0-9.\-_:]*));?/'; + // 1. hex 2. dec 3. string (XML style) /** * Decimal to parsed string conversion table for special entities. + * @type array */ protected $_special_dec2str = array( @@ -3668,6 +4442,7 @@ class HTMLPurifier_EntityParser /** * Stripped entity names to decimal conversion table for special entities. + * @type array */ protected $_special_ent2dec = array( @@ -3682,41 +4457,45 @@ class HTMLPurifier_EntityParser * running this whenever you have parsed character is t3h 5uck, we run * it before everything else. * - * @param $string String to have non-special entities parsed. - * @returns Parsed string. + * @param string $string String to have non-special entities parsed. + * @return string Parsed string. */ - public function substituteNonSpecialEntities($string) { + public function substituteNonSpecialEntities($string) + { // it will try to detect missing semicolons, but don't rely on it return preg_replace_callback( $this->_substituteEntitiesRegex, array($this, 'nonSpecialEntityCallback'), $string - ); + ); } /** * Callback function for substituteNonSpecialEntities() that does the work. * - * @param $matches PCRE matches array, with 0 the entire match, and + * @param array $matches PCRE matches array, with 0 the entire match, and * either index 1, 2 or 3 set with a hex value, dec value, * or string (respectively). - * @returns Replacement string. + * @return string Replacement string. */ - protected function nonSpecialEntityCallback($matches) { + protected function nonSpecialEntityCallback($matches) + { // replaces all but big five $entity = $matches[0]; $is_num = (@$matches[0][1] === '#'); if ($is_num) { $is_hex = (@$entity[2] === 'x'); $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2]; - // abort for special characters - if (isset($this->_special_dec2str[$code])) return $entity; - + if (isset($this->_special_dec2str[$code])) { + return $entity; + } return HTMLPurifier_Encoder::unichr($code); } else { - if (isset($this->_special_ent2dec[$matches[3]])) return $entity; + if (isset($this->_special_ent2dec[$matches[3]])) { + return $entity; + } if (!$this->_entity_lookup) { $this->_entity_lookup = HTMLPurifier_EntityLookup::instance(); } @@ -3734,14 +4513,16 @@ protected function nonSpecialEntityCallback($matches) { * @notice We try to avoid calling this function because otherwise, it * would have to be called a lot (for every parsed section). * - * @param $string String to have non-special entities parsed. - * @returns Parsed string. + * @param string $string String to have non-special entities parsed. + * @return string Parsed string. */ - public function substituteSpecialEntities($string) { + public function substituteSpecialEntities($string) + { return preg_replace_callback( $this->_substituteEntitiesRegex, array($this, 'specialEntityCallback'), - $string); + $string + ); } /** @@ -3749,12 +4530,13 @@ public function substituteSpecialEntities($string) { * * This callback has same syntax as nonSpecialEntityCallback(). * - * @param $matches PCRE-style matches array, with 0 the entire match, and + * @param array $matches PCRE-style matches array, with 0 the entire match, and * either index 1, 2 or 3 set with a hex value, dec value, * or string (respectively). - * @returns Replacement string. + * @return string Replacement string. */ - protected function specialEntityCallback($matches) { + protected function specialEntityCallback($matches) + { $entity = $matches[0]; $is_num = (@$matches[0][1] === '#'); if ($is_num) { @@ -3769,7 +4551,6 @@ protected function specialEntityCallback($matches) { $entity; } } - } @@ -3792,16 +4573,46 @@ class HTMLPurifier_ErrorCollector const MESSAGE = 2; const CHILDREN = 3; + /** + * @type array + */ protected $errors; + + /** + * @type array + */ protected $_current; + + /** + * @type array + */ protected $_stacks = array(array()); + + /** + * @type HTMLPurifier_Language + */ protected $locale; + + /** + * @type HTMLPurifier_Generator + */ protected $generator; + + /** + * @type HTMLPurifier_Context + */ protected $context; + /** + * @type array + */ protected $lines = array(); - public function __construct($context) { + /** + * @param HTMLPurifier_Context $context + */ + public function __construct($context) + { $this->locale =& $context->get('Locale'); $this->context = $context; $this->_current =& $this->_stacks[0]; @@ -3810,13 +4621,11 @@ public function __construct($context) { /** * Sends an error message to the collector for later use - * @param $severity int Error severity, PHP error style (don't use E_USER_) - * @param $msg string Error message text - * @param $subst1 string First substitution for $msg - * @param $subst2 string ... + * @param int $severity Error severity, PHP error style (don't use E_USER_) + * @param string $msg Error message text */ - public function send($severity, $msg) { - + public function send($severity, $msg) + { $args = array(); if (func_num_args() > 2) { $args = func_get_args(); @@ -3826,7 +4635,7 @@ public function send($severity, $msg) { $token = $this->context->get('CurrentToken', true); $line = $token ? $token->line : $this->context->get('CurrentLine', true); - $col = $token ? $token->col : $this->context->get('CurrentCol', true); + $col = $token ? $token->col : $this->context->get('CurrentCol', true); $attr = $this->context->get('CurrentAttr', true); // perform special substitutions, also add custom parameters @@ -3836,7 +4645,9 @@ public function send($severity, $msg) { } if (!is_null($attr)) { $subst['$CurrentAttr.Name'] = $attr; - if (isset($token->attr[$attr])) $subst['$CurrentAttr.Value'] = $token->attr[$attr]; + if (isset($token->attr[$attr])) { + $subst['$CurrentAttr.Value'] = $token->attr[$attr]; + } } if (empty($args)) { @@ -3845,7 +4656,9 @@ public function send($severity, $msg) { $msg = $this->locale->formatMessage($msg, $args); } - if (!empty($subst)) $msg = strtr($msg, $subst); + if (!empty($subst)) { + $msg = strtr($msg, $subst); + } // (numerically indexed) $error = array( @@ -3856,16 +4669,15 @@ public function send($severity, $msg) { ); $this->_current[] = $error; - // NEW CODE BELOW ... - - $struct = null; // Top-level errors are either: // TOKEN type, if $value is set appropriately, or // "syntax" type, if $value is null $new_struct = new HTMLPurifier_ErrorStruct(); $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN; - if ($token) $new_struct->value = clone $token; + if ($token) { + $new_struct->value = clone $token; + } if (is_int($line) && is_int($col)) { if (isset($this->lines[$line][$col])) { $struct = $this->lines[$line][$col]; @@ -3904,30 +4716,34 @@ public function send($severity, $msg) { /** * Retrieves raw error data for custom formatter to use - * @param List of arrays in format of array(line of error, - * error severity, error message, - * recursive sub-errors array) */ - public function getRaw() { + public function getRaw() + { return $this->errors; } /** * Default HTML formatting implementation for error messages - * @param $config Configuration array, vital for HTML output nature - * @param $errors Errors array to display; used for recursion. + * @param HTMLPurifier_Config $config Configuration, vital for HTML output nature + * @param array $errors Errors array to display; used for recursion. + * @return string */ - public function getHTMLFormatted($config, $errors = null) { + public function getHTMLFormatted($config, $errors = null) + { $ret = array(); $this->generator = new HTMLPurifier_Generator($config, $this->context); - if ($errors === null) $errors = $this->errors; + if ($errors === null) { + $errors = $this->errors; + } // 'At line' message needs to be removed // generation code for new structure goes here. It needs to be recursive. foreach ($this->lines as $line => $col_array) { - if ($line == -1) continue; + if ($line == -1) { + continue; + } foreach ($col_array as $col => $struct) { $this->_renderStruct($ret, $struct, $line, $col); } @@ -3944,7 +4760,8 @@ public function getHTMLFormatted($config, $errors = null) { } - private function _renderStruct(&$ret, $struct, $line = null, $col = null) { + private function _renderStruct(&$ret, $struct, $line = null, $col = null) + { $stack = array($struct); $context_stack = array(array()); while ($current = array_pop($stack)) { @@ -3970,7 +4787,7 @@ private function _renderStruct(&$ret, $struct, $line = null, $col = null) { //$string .= ''; $ret[] = $string; } - foreach ($current->children as $type => $array) { + foreach ($current->children as $array) { $context[] = $current; $stack = array_merge($stack, array_reverse($array, true)); for ($i = count($array); $i > 0; $i--) { @@ -3979,7 +4796,6 @@ private function _renderStruct(&$ret, $struct, $line = null, $col = null) { } } } - } @@ -4005,6 +4821,7 @@ class HTMLPurifier_ErrorStruct /** * Type of this struct. + * @type string */ public $type; @@ -4014,11 +4831,13 @@ class HTMLPurifier_ErrorStruct * - TOKEN: Instance of HTMLPurifier_Token * - ATTR: array('attr-name', 'value') * - CSSPROP: array('prop-name', 'value') + * @type mixed */ public $value; /** * Errors registered for this structure. + * @type array */ public $errors = array(); @@ -4026,10 +4845,17 @@ class HTMLPurifier_ErrorStruct * Child ErrorStructs that are from this structure. For example, a TOKEN * ErrorStruct would contain ATTR ErrorStructs. This is a multi-dimensional * array in structure: [TYPE]['identifier'] + * @type array */ public $children = array(); - public function getChild($type, $id) { + /** + * @param string $type + * @param string $id + * @return mixed + */ + public function getChild($type, $id) + { if (!isset($this->children[$type][$id])) { $this->children[$type][$id] = new HTMLPurifier_ErrorStruct(); $this->children[$type][$id]->type = $type; @@ -4037,10 +4863,14 @@ public function getChild($type, $id) { return $this->children[$type][$id]; } - public function addError($severity, $message) { + /** + * @param int $severity + * @param string $message + */ + public function addError($severity, $message) + { $this->errors[] = array($severity, $message); } - } @@ -4083,24 +4913,34 @@ class HTMLPurifier_Filter { /** - * Name of the filter for identification purposes + * Name of the filter for identification purposes. + * @type string */ public $name; /** * Pre-processor function, handles HTML before HTML Purifier + * @param string $html + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string */ - public function preFilter($html, $config, $context) { + public function preFilter($html, $config, $context) + { return $html; } /** * Post-processor function, handles HTML after HTML Purifier + * @param string $html + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string */ - public function postFilter($html, $config, $context) { + public function postFilter($html, $config, $context) + { return $html; } - } @@ -4118,52 +4958,61 @@ class HTMLPurifier_Generator { /** - * Whether or not generator should produce XML output + * Whether or not generator should produce XML output. + * @type bool */ private $_xhtml = true; /** - * :HACK: Whether or not generator should comment the insides of )#si', - array($this, 'scriptCallback'), $html); + $html = preg_replace_callback( + '#(]*>)(\s*[^<].+?)()#si', + array($this, 'scriptCallback'), + $html + ); } $html = $this->normalize($html, $config, $context); @@ -15054,15 +18727,15 @@ public function tokenizeHTML($html, $config, $context) { if ($maintain_line_numbers) { $current_line = 1; - $current_col = 0; + $current_col = 0; $length = strlen($html); } else { $current_line = false; - $current_col = false; + $current_col = false; $length = false; } $context->register('CurrentLine', $current_line); - $context->register('CurrentCol', $current_col); + $context->register('CurrentCol', $current_col); $nl = "\n"; // how often to manually recalculate. This will ALWAYS be right, // but it's pretty wasteful. Set to 0 to turn off @@ -15076,16 +18749,14 @@ public function tokenizeHTML($html, $config, $context) { // for testing synchronization $loops = 0; - while(++$loops) { - + while (++$loops) { // $cursor is either at the start of a token, or inside of // a tag (i.e. there was a < immediately before it), as indicated // by $inside_tag if ($maintain_line_numbers) { - // $rcursor, however, is always at the start of a token. - $rcursor = $cursor - (int) $inside_tag; + $rcursor = $cursor - (int)$inside_tag; // Column number is cheap, so we calculate it every round. // We're interested at the *end* of the newline string, so @@ -15095,14 +18766,11 @@ public function tokenizeHTML($html, $config, $context) { $current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1); // recalculate lines - if ( - $synchronize_interval && // synchronization is on - $cursor > 0 && // cursor is further than zero - $loops % $synchronize_interval === 0 // time to synchronize! - ) { + if ($synchronize_interval && // synchronization is on + $cursor > 0 && // cursor is further than zero + $loops % $synchronize_interval === 0) { // time to synchronize! $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor); } - } $position_next_lt = strpos($html, '<', $cursor); @@ -15118,35 +18786,42 @@ public function tokenizeHTML($html, $config, $context) { if (!$inside_tag && $position_next_lt !== false) { // We are not inside tag and there still is another tag to parse $token = new - HTMLPurifier_Token_Text( - $this->parseData( - substr( - $html, $cursor, $position_next_lt - $cursor - ) + HTMLPurifier_Token_Text( + $this->parseData( + substr( + $html, + $cursor, + $position_next_lt - $cursor ) - ); + ) + ); if ($maintain_line_numbers) { $token->rawPosition($current_line, $current_col); $current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor); } $array[] = $token; - $cursor = $position_next_lt + 1; + $cursor = $position_next_lt + 1; $inside_tag = true; continue; } elseif (!$inside_tag) { // We are not inside tag but there are no more tags // If we're already at the end, break - if ($cursor === strlen($html)) break; + if ($cursor === strlen($html)) { + break; + } // Create Text of rest of string $token = new - HTMLPurifier_Token_Text( - $this->parseData( - substr( - $html, $cursor - ) + HTMLPurifier_Token_Text( + $this->parseData( + substr( + $html, + $cursor ) - ); - if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); + ) + ); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + } $array[] = $token; break; } elseif ($inside_tag && $position_next_gt !== false) { @@ -15170,16 +18845,16 @@ public function tokenizeHTML($html, $config, $context) { } // Check if it's a comment - if ( - substr($segment, 0, 3) === '!--' - ) { + if (substr($segment, 0, 3) === '!--') { // re-determine segment length, looking for --> $position_comment_end = strpos($html, '-->', $cursor); if ($position_comment_end === false) { // uh oh, we have a comment that extends to // infinity. Can't be helped: set comment // end position to end of string - if ($e) $e->send(E_WARNING, 'Lexer: Unclosed comment'); + if ($e) { + $e->send(E_WARNING, 'Lexer: Unclosed comment'); + } $position_comment_end = strlen($html); $end = true; } else { @@ -15188,11 +18863,13 @@ public function tokenizeHTML($html, $config, $context) { $strlen_segment = $position_comment_end - $cursor; $segment = substr($html, $cursor, $strlen_segment); $token = new - HTMLPurifier_Token_Comment( - substr( - $segment, 3, $strlen_segment - 3 - ) - ); + HTMLPurifier_Token_Comment( + substr( + $segment, + 3, + $strlen_segment - 3 + ) + ); if ($maintain_line_numbers) { $token->rawPosition($current_line, $current_col); $current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment); @@ -15204,7 +18881,7 @@ public function tokenizeHTML($html, $config, $context) { } // Check if it's an end tag - $is_end_tag = (strpos($segment,'/') === 0); + $is_end_tag = (strpos($segment, '/') === 0); if ($is_end_tag) { $type = substr($segment, 1); $token = new HTMLPurifier_Token_End($type); @@ -15223,7 +18900,9 @@ public function tokenizeHTML($html, $config, $context) { // text and go our merry way if (!ctype_alpha($segment[0])) { // XML: $segment[0] !== '_' && $segment[0] !== ':' - if ($e) $e->send(E_NOTICE, 'Lexer: Unescaped lt'); + if ($e) { + $e->send(E_NOTICE, 'Lexer: Unescaped lt'); + } $token = new HTMLPurifier_Token_Text('<'); if ($maintain_line_numbers) { $token->rawPosition($current_line, $current_col); @@ -15238,7 +18917,7 @@ public function tokenizeHTML($html, $config, $context) { // trailing slash. Remember, we could have a tag like
      , so // any later token processing scripts must convert improperly // classified EmptyTags from StartTags. - $is_self_closing = (strrpos($segment,'/') === $strlen_segment-1); + $is_self_closing = (strrpos($segment, '/') === $strlen_segment - 1); if ($is_self_closing) { $strlen_segment--; $segment = substr($segment, 0, $strlen_segment); @@ -15268,14 +18947,16 @@ public function tokenizeHTML($html, $config, $context) { $attribute_string = trim( substr( - $segment, $position_first_space + $segment, + $position_first_space ) ); if ($attribute_string) { $attr = $this->parseAttributeString( - $attribute_string - , $config, $context - ); + $attribute_string, + $config, + $context + ); } else { $attr = array(); } @@ -15295,15 +18976,19 @@ public function tokenizeHTML($html, $config, $context) { continue; } else { // inside tag, but there's no ending > sign - if ($e) $e->send(E_WARNING, 'Lexer: Missing gt'); + if ($e) { + $e->send(E_WARNING, 'Lexer: Missing gt'); + } $token = new - HTMLPurifier_Token_Text( - '<' . - $this->parseData( - substr($html, $cursor) - ) - ); - if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); + HTMLPurifier_Token_Text( + '<' . + $this->parseData( + substr($html, $cursor) + ) + ); + if ($maintain_line_numbers) { + $token->rawPosition($current_line, $current_col); + } // no cursor scroll? Hmm... $array[] = $token; break; @@ -15318,8 +19003,14 @@ public function tokenizeHTML($html, $config, $context) { /** * PHP 5.0.x compatible substr_count that implements offset and length - */ - protected function substrCount($haystack, $needle, $offset, $length) { + * @param string $haystack + * @param string $needle + * @param int $offset + * @param int $length + * @return int + */ + protected function substrCount($haystack, $needle, $offset, $length) + { static $oldVersion; if ($oldVersion === null) { $oldVersion = version_compare(PHP_VERSION, '5.1', '<'); @@ -15335,13 +19026,18 @@ protected function substrCount($haystack, $needle, $offset, $length) { /** * Takes the inside of an HTML tag and makes an assoc array of attributes. * - * @param $string Inside of tag excluding name. - * @returns Assoc array of attributes. + * @param string $string Inside of tag excluding name. + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array Assoc array of attributes. */ - public function parseAttributeString($string, $config, $context) { - $string = (string) $string; // quick typecast + public function parseAttributeString($string, $config, $context) + { + $string = (string)$string; // quick typecast - if ($string == '') return array(); // no attributes + if ($string == '') { + return array(); + } // no attributes $e = false; if ($config->get('Core.CollectErrors')) { @@ -15360,46 +19056,55 @@ public function parseAttributeString($string, $config, $context) { list($key, $quoted_value) = explode('=', $string); $quoted_value = trim($quoted_value); if (!$key) { - if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing attribute key'); + } return array(); } - if (!$quoted_value) return array($key => ''); + if (!$quoted_value) { + return array($key => ''); + } $first_char = @$quoted_value[0]; - $last_char = @$quoted_value[strlen($quoted_value)-1]; + $last_char = @$quoted_value[strlen($quoted_value) - 1]; $same_quote = ($first_char == $last_char); $open_quote = ($first_char == '"' || $first_char == "'"); - if ( $same_quote && $open_quote) { + if ($same_quote && $open_quote) { // well behaved $value = substr($quoted_value, 1, strlen($quoted_value) - 2); } else { // not well behaved if ($open_quote) { - if ($e) $e->send(E_ERROR, 'Lexer: Missing end quote'); + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing end quote'); + } $value = substr($quoted_value, 1); } else { $value = $quoted_value; } } - if ($value === false) $value = ''; + if ($value === false) { + $value = ''; + } return array($key => $this->parseData($value)); } // setup loop environment - $array = array(); // return assoc array of attributes + $array = array(); // return assoc array of attributes $cursor = 0; // current position in string (moves forward) - $size = strlen($string); // size of the string (stays the same) + $size = strlen($string); // size of the string (stays the same) // if we have unquoted attributes, the parser expects a terminating // space, so let's guarantee that there's always a terminating space. $string .= ' '; - while(true) { - - if ($cursor >= $size) { - break; + $old_cursor = -1; + while ($cursor < $size) { + if ($old_cursor >= $cursor) { + throw new Exception("Infinite loop detected"); } + $old_cursor = $cursor; $cursor += ($value = strspn($string, $this->_whitespace, $cursor)); // grab the key @@ -15414,8 +19119,10 @@ public function parseAttributeString($string, $config, $context) { $key = substr($string, $key_begin, $key_end - $key_begin); if (!$key) { - if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); - $cursor += strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing attribute key'); + } + $cursor += 1 + strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop continue; // empty key } @@ -15466,24 +19173,177 @@ public function parseAttributeString($string, $config, $context) { } $value = substr($string, $value_begin, $value_end - $value_begin); - if ($value === false) $value = ''; + if ($value === false) { + $value = ''; + } $array[$key] = $this->parseData($value); $cursor++; - } else { // boolattr if ($key !== '') { $array[$key] = $key; } else { // purely theoretical - if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); + if ($e) { + $e->send(E_ERROR, 'Lexer: Missing attribute key'); + } } - } } return $array; } +} + + + + + +/** + * Concrete comment node class. + */ +class HTMLPurifier_Node_Comment extends HTMLPurifier_Node +{ + /** + * Character data within comment. + * @type string + */ + public $data; + + /** + * @type bool + */ + public $is_whitespace = true; + + /** + * Transparent constructor. + * + * @param string $data String comment data. + * @param int $line + * @param int $col + */ + public function __construct($data, $line = null, $col = null) + { + $this->data = $data; + $this->line = $line; + $this->col = $col; + } + + public function toTokenPair() { + return array(new HTMLPurifier_Token_Comment($this->data, $this->line, $this->col), null); + } +} + + + +/** + * Concrete element node class. + */ +class HTMLPurifier_Node_Element extends HTMLPurifier_Node +{ + /** + * The lower-case name of the tag, like 'a', 'b' or 'blockquote'. + * + * @note Strictly speaking, XML tags are case sensitive, so we shouldn't + * be lower-casing them, but these tokens cater to HTML tags, which are + * insensitive. + * @type string + */ + public $name; + + /** + * Associative array of the node's attributes. + * @type array + */ + public $attr = array(); + + /** + * List of child elements. + * @type array + */ + public $children = array(); + + /** + * Does this use the form or the form, i.e. + * is it a pair of start/end tokens or an empty token. + * @bool + */ + public $empty = false; + + public $endCol = null, $endLine = null, $endArmor = array(); + + public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) { + $this->name = $name; + $this->attr = $attr; + $this->line = $line; + $this->col = $col; + $this->armor = $armor; + } + + public function toTokenPair() { + // XXX inefficiency here, normalization is not necessary + if ($this->empty) { + return array(new HTMLPurifier_Token_Empty($this->name, $this->attr, $this->line, $this->col, $this->armor), null); + } else { + $start = new HTMLPurifier_Token_Start($this->name, $this->attr, $this->line, $this->col, $this->armor); + $end = new HTMLPurifier_Token_End($this->name, array(), $this->endLine, $this->endCol, $this->endArmor); + //$end->start = $start; + return array($start, $end); + } + } +} + + + + +/** + * Concrete text token class. + * + * Text tokens comprise of regular parsed character data (PCDATA) and raw + * character data (from the CDATA sections). Internally, their + * data is parsed with all entities expanded. Surprisingly, the text token + * does have a "tag name" called #PCDATA, which is how the DTD represents it + * in permissible child nodes. + */ +class HTMLPurifier_Node_Text extends HTMLPurifier_Node +{ + + /** + * PCDATA tag name compatible with DTD, see + * HTMLPurifier_ChildDef_Custom for details. + * @type string + */ + public $name = '#PCDATA'; + + /** + * @type string + */ + public $data; + /**< Parsed character data of text. */ + + /** + * @type bool + */ + public $is_whitespace; + + /**< Bool indicating if node is whitespace. */ + + /** + * Constructor, accepts data and determines if it is whitespace. + * @param string $data String parsed character data. + * @param int $line + * @param int $col + */ + public function __construct($data, $is_whitespace, $line = null, $col = null) + { + $this->data = $data; + $this->is_whitespace = $is_whitespace; + $this->line = $line; + $this->col = $col; + } + public function toTokenPair() { + return array(new HTMLPurifier_Token_Text($this->data, $this->line, $this->col), null); + } } @@ -15498,16 +19358,23 @@ abstract class HTMLPurifier_Strategy_Composite extends HTMLPurifier_Strategy /** * List of strategies to run tokens through. + * @type HTMLPurifier_Strategy[] */ protected $strategies = array(); - public function execute($tokens, $config, $context) { + /** + * @param HTMLPurifier_Token[] $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_Token[] + */ + public function execute($tokens, $config, $context) + { foreach ($this->strategies as $strategy) { $tokens = $strategy->execute($tokens, $config, $context); } return $tokens; } - } @@ -15519,14 +19386,13 @@ public function execute($tokens, $config, $context) { */ class HTMLPurifier_Strategy_Core extends HTMLPurifier_Strategy_Composite { - - public function __construct() { + public function __construct() + { $this->strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements(); $this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed(); $this->strategies[] = new HTMLPurifier_Strategy_FixNesting(); $this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes(); } - } @@ -15543,12 +19409,12 @@ public function __construct() { * document type definitions, such as the chameleon nature of ins/del * tags and global child exclusions. * - * The first major objective of this strategy is to iterate through all the - * nodes (not tokens) of the list of tokens and determine whether or not - * their children conform to the element's definition. If they do not, the - * child definition may optionally supply an amended list of elements that - * is valid or require that the entire node be deleted (and the previous - * node rescanned). + * The first major objective of this strategy is to iterate through all + * the nodes and determine whether or not their children conform to the + * element's definition. If they do not, the child definition may + * optionally supply an amended list of elements that is valid or + * require that the entire node be deleted (and the previous node + * rescanned). * * The second objective is to ensure that explicitly excluded elements of * an element do not appear in its children. Code that accomplishes this @@ -15558,43 +19424,34 @@ public function __construct() { * @note Whether or not unrecognized children are silently dropped or * translated into text depends on the child definitions. * - * @todo Enable nodes to be bubbled out of the structure. - * - * @warning This algorithm (though it may be hard to see) proceeds from - * a top-down fashion. Thus, parents are processed before - * children. This is easy to implement and has a nice effiency - * benefit, in that if a node is removed, we never waste any - * time processing it, but it also means that if a child - * changes in a non-encapsulated way (e.g. it is removed), we - * need to go back and reprocess the parent to see if those - * changes resulted in problems for the parent. See - * [BACKTRACK] for an example of this. In the current - * implementation, this backtracking can only be triggered when - * a node is removed and if that node was the sole node, the - * parent would need to be removed. As such, it is easy to see - * that backtracking only incurs constant overhead. If more - * sophisticated backtracking is implemented, care must be - * taken to avoid nontermination or exponential blowup. + * @todo Enable nodes to be bubbled out of the structure. This is + * easier with our new algorithm. */ class HTMLPurifier_Strategy_FixNesting extends HTMLPurifier_Strategy { - public function execute($tokens, $config, $context) { + /** + * @param HTMLPurifier_Token[] $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array|HTMLPurifier_Token[] + */ + public function execute($tokens, $config, $context) + { + //####################################################################// // Pre-processing + // O(n) pass to convert to a tree, so that we can efficiently + // refer to substrings + $top_node = HTMLPurifier_Arborize::arborize($tokens, $config, $context); + // get a copy of the HTML definition $definition = $config->getHTMLDefinition(); $excludes_enabled = !$config->get('Core.DisableExcludes'); - // insert implicit "parent" node, will be removed at end. - // DEFINITION CALL - $parent_name = $definition->info_parent; - array_unshift($tokens, new HTMLPurifier_Token_Start($parent_name)); - $tokens[] = new HTMLPurifier_Token_End($parent_name); - // setup the context variable 'IsInline', for chameleon processing // is 'false' when we are not inline, 'true' when it must always // be inline, and an integer when it is inline for a certain @@ -15608,272 +19465,116 @@ public function execute($tokens, $config, $context) { //####################################################################// // Loop initialization - // stack that contains the indexes of all parents, - // $stack[count($stack)-1] being the current parent - $stack = array(); - // stack that contains all elements that are excluded // it is organized by parent elements, similar to $stack, // but it is only populated when an element with exclusions is // processed, i.e. there won't be empty exclusions. - $exclude_stack = array(); + $exclude_stack = array($definition->info_parent_def->excludes); // variable that contains the start token while we are processing // nodes. This enables error reporting to do its job - $start_token = false; - $context->register('CurrentToken', $start_token); + $node = $top_node; + // dummy token + list($token, $d) = $node->toTokenPair(); + $context->register('CurrentNode', $node); + $context->register('CurrentToken', $token); //####################################################################// // Loop - // iterate through all start nodes. Determining the start node - // is complicated so it has been omitted from the loop construct - for ($i = 0, $size = count($tokens) ; $i < $size; ) { - - //################################################################// - // Gather information on children - - // child token accumulator - $child_tokens = array(); - - // scroll to the end of this node, report number, and collect - // all children - for ($j = $i, $depth = 0; ; $j++) { - if ($tokens[$j] instanceof HTMLPurifier_Token_Start) { - $depth++; - // skip token assignment on first iteration, this is the - // token we currently are on - if ($depth == 1) continue; - } elseif ($tokens[$j] instanceof HTMLPurifier_Token_End) { - $depth--; - // skip token assignment on last iteration, this is the - // end token of the token we're currently on - if ($depth == 0) break; - } - $child_tokens[] = $tokens[$j]; - } - - // $i is index of start token - // $j is index of end token - - $start_token = $tokens[$i]; // to make token available via CurrentToken - - //################################################################// - // Gather information on parent - - // calculate parent information - if ($count = count($stack)) { - $parent_index = $stack[$count-1]; - $parent_name = $tokens[$parent_index]->name; - if ($parent_index == 0) { - $parent_def = $definition->info_parent_def; - } else { - $parent_def = $definition->info[$parent_name]; - } - } else { - // processing as if the parent were the "root" node - // unknown info, it won't be used anyway, in the future, - // we may want to enforce one element only (this is - // necessary for HTML Purifier to clean entire documents - $parent_index = $parent_name = $parent_def = null; - } - - // calculate context - if ($is_inline === false) { - // check if conditions make it inline - if (!empty($parent_def) && $parent_def->descendants_are_inline) { - $is_inline = $count - 1; - } - } else { - // check if we're out of inline - if ($count === $is_inline) { - $is_inline = false; - } - } - - //################################################################// - // Determine whether element is explicitly excluded SGML-style + // We need to implement a post-order traversal iteratively, to + // avoid running into stack space limits. This is pretty tricky + // to reason about, so we just manually stack-ify the recursive + // variant: + // + // function f($node) { + // foreach ($node->children as $child) { + // f($child); + // } + // validate($node); + // } + // + // Thus, we will represent a stack frame as array($node, + // $is_inline, stack of children) + // e.g. array_reverse($node->children) - already processed + // children. + + $parent_def = $definition->info_parent_def; + $stack = array( + array($top_node, + $parent_def->descendants_are_inline, + $parent_def->excludes, // exclusions + 0) + ); - // determine whether or not element is excluded by checking all - // parent exclusions. The array should not be very large, two - // elements at most. - $excluded = false; - if (!empty($exclude_stack) && $excludes_enabled) { - foreach ($exclude_stack as $lookup) { - if (isset($lookup[$tokens[$i]->name])) { - $excluded = true; - // no need to continue processing - break; - } + while (!empty($stack)) { + list($node, $is_inline, $excludes, $ix) = array_pop($stack); + // recursive call + $go = false; + $def = empty($stack) ? $definition->info_parent_def : $definition->info[$node->name]; + while (isset($node->children[$ix])) { + $child = $node->children[$ix++]; + if ($child instanceof HTMLPurifier_Node_Element) { + $go = true; + $stack[] = array($node, $is_inline, $excludes, $ix); + $stack[] = array($child, + // ToDo: I don't think it matters if it's def or + // child_def, but double check this... + $is_inline || $def->descendants_are_inline, + empty($def->excludes) ? $excludes + : array_merge($excludes, $def->excludes), + 0); + break; } - } - - //################################################################// - // Perform child validation - - if ($excluded) { - // there is an exclusion, remove the entire node - $result = false; - $excludes = array(); // not used, but good to initialize anyway + }; + if ($go) continue; + list($token, $d) = $node->toTokenPair(); + // base case + if ($excludes_enabled && isset($excludes[$node->name])) { + $node->dead = true; + if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded'); } else { - // DEFINITION CALL - if ($i === 0) { - // special processing for the first node - $def = $definition->info_parent_def; - } else { - $def = $definition->info[$tokens[$i]->name]; - + // XXX I suppose it would be slightly more efficient to + // avoid the allocation here and have children + // strategies handle it + $children = array(); + foreach ($node->children as $child) { + if (!$child->dead) $children[] = $child; } - - if (!empty($def->child)) { - // have DTD child def validate children - $result = $def->child->validateChildren( - $child_tokens, $config, $context); + $result = $def->child->validateChildren($children, $config, $context); + if ($result === true) { + // nop + $node->children = $children; + } elseif ($result === false) { + $node->dead = true; + if ($e) $e->send(E_ERROR, 'Strategy_FixNesting: Node removed'); } else { - // weird, no child definition, get rid of everything - $result = false; - } - - // determine whether or not this element has any exclusions - $excludes = $def->excludes; - } - - // $result is now a bool or array - - //################################################################// - // Process result by interpreting $result - - if ($result === true || $child_tokens === $result) { - // leave the node as is - - // register start token as a parental node start - $stack[] = $i; - - // register exclusions if there are any - if (!empty($excludes)) $exclude_stack[] = $excludes; - - // move cursor to next possible start node - $i++; - - } elseif($result === false) { - // remove entire node - - if ($e) { - if ($excluded) { - $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded'); - } else { - $e->send(E_ERROR, 'Strategy_FixNesting: Node removed'); - } - } - - // calculate length of inner tokens and current tokens - $length = $j - $i + 1; - - // perform removal - array_splice($tokens, $i, $length); - - // update size - $size -= $length; - - // there is no start token to register, - // current node is now the next possible start node - // unless it turns out that we need to do a double-check - - // this is a rought heuristic that covers 100% of HTML's - // cases and 99% of all other cases. A child definition - // that would be tricked by this would be something like: - // ( | a b c) where it's all or nothing. Fortunately, - // our current implementation claims that that case would - // not allow empty, even if it did - if (!$parent_def->child->allow_empty) { - // we need to do a double-check [BACKTRACK] - $i = $parent_index; - array_pop($stack); - } - - // PROJECTED OPTIMIZATION: Process all children elements before - // reprocessing parent node. - - } else { - // replace node with $result - - // calculate length of inner tokens - $length = $j - $i - 1; - - if ($e) { - if (empty($result) && $length) { - $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed'); - } else { - $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized'); - } - } - - // perform replacement - array_splice($tokens, $i + 1, $length, $result); - - // update size - $size -= $length; - $size += count($result); - - // register start token as a parental node start - $stack[] = $i; - - // register exclusions if there are any - if (!empty($excludes)) $exclude_stack[] = $excludes; - - // move cursor to next possible start node - $i++; - - } - - //################################################################// - // Scroll to next start node - - // We assume, at this point, that $i is the index of the token - // that is the first possible new start point for a node. - - // Test if the token indeed is a start tag, if not, move forward - // and test again. - $size = count($tokens); - while ($i < $size and !$tokens[$i] instanceof HTMLPurifier_Token_Start) { - if ($tokens[$i] instanceof HTMLPurifier_Token_End) { - // pop a token index off the stack if we ended a node - array_pop($stack); - // pop an exclusion lookup off exclusion stack if - // we ended node and that node had exclusions - if ($i == 0 || $i == $size - 1) { - // use specialized var if it's the super-parent - $s_excludes = $definition->info_parent_def->excludes; - } else { - $s_excludes = $definition->info[$tokens[$i]->name]->excludes; - } - if ($s_excludes) { - array_pop($exclude_stack); + $node->children = $result; + if ($e) { + // XXX This will miss mutations of internal nodes. Perhaps defer to the child validators + if (empty($result) && !empty($children)) { + $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed'); + } else if ($result != $children) { + $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized'); + } } } - $i++; } - } //####################################################################// // Post-processing - // remove implicit parent tokens at the beginning and end - array_shift($tokens); - array_pop($tokens); - // remove context variables $context->destroy('IsInline'); + $context->destroy('CurrentNode'); $context->destroy('CurrentToken'); //####################################################################// // Return - return $tokens; - + return HTMLPurifier_Arborize::flatten($node, $config, $context); } - } @@ -15896,66 +19597,83 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy /** * Array stream of tokens being processed. + * @type HTMLPurifier_Token[] */ protected $tokens; /** - * Current index in $tokens. + * Current token. + * @type HTMLPurifier_Token + */ + protected $token; + + /** + * Zipper managing the true state. + * @type HTMLPurifier_Zipper */ - protected $t; + protected $zipper; /** * Current nesting of elements. + * @type array */ protected $stack; /** * Injectors active in this stream processing. + * @type HTMLPurifier_Injector[] */ protected $injectors; /** * Current instance of HTMLPurifier_Config. + * @type HTMLPurifier_Config */ protected $config; /** * Current instance of HTMLPurifier_Context. + * @type HTMLPurifier_Context */ protected $context; - public function execute($tokens, $config, $context) { - + /** + * @param HTMLPurifier_Token[] $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_Token[] + * @throws HTMLPurifier_Exception + */ + public function execute($tokens, $config, $context) + { $definition = $config->getHTMLDefinition(); // local variables $generator = new HTMLPurifier_Generator($config, $context); $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); // used for autoclose early abortion - $global_parent_allowed_elements = array(); - if (isset($definition->info[$definition->info_parent])) { - // may be unset under testing circumstances - $global_parent_allowed_elements = $definition->info[$definition->info_parent]->child->getAllowedElements($config); - } + $global_parent_allowed_elements = $definition->info_parent_def->child->getAllowedElements($config); $e = $context->get('ErrorCollector', true); - $t = false; // token index $i = false; // injector index - $token = false; // the current token - $reprocess = false; // whether or not to reprocess the same token + list($zipper, $token) = HTMLPurifier_Zipper::fromArray($tokens); + if ($token === NULL) { + return array(); + } + $reprocess = false; // whether or not to reprocess the same token $stack = array(); // member variables - $this->stack =& $stack; - $this->t =& $t; - $this->tokens =& $tokens; - $this->config = $config; + $this->stack =& $stack; + $this->tokens =& $tokens; + $this->token =& $token; + $this->zipper =& $zipper; + $this->config = $config; $this->context = $context; // context variables $context->register('CurrentNesting', $stack); - $context->register('InputIndex', $t); - $context->register('InputTokens', $tokens); - $context->register('CurrentToken', $token); + $context->register('InputZipper', $zipper); + $context->register('CurrentToken', $token); // -- begin INJECTOR -- @@ -15967,9 +19685,13 @@ public function execute($tokens, $config, $context) { unset($injectors['Custom']); // special case foreach ($injectors as $injector => $b) { // XXX: Fix with a legitimate lookup table of enabled filters - if (strpos($injector, '.') !== false) continue; + if (strpos($injector, '.') !== false) { + continue; + } $injector = "HTMLPurifier_Injector_$injector"; - if (!$b) continue; + if (!$b) { + continue; + } $this->injectors[] = new $injector; } foreach ($def_injectors as $injector) { @@ -15977,7 +19699,9 @@ public function execute($tokens, $config, $context) { $this->injectors[] = $injector; } foreach ($custom_injectors as $injector) { - if (!$injector) continue; + if (!$injector) { + continue; + } if (is_string($injector)) { $injector = "HTMLPurifier_Injector_$injector"; $injector = new $injector; @@ -15989,7 +19713,9 @@ public function execute($tokens, $config, $context) { // variables for performance reasons foreach ($this->injectors as $ix => $injector) { $error = $injector->prepare($config, $context); - if (!$error) continue; + if (!$error) { + continue; + } array_splice($this->injectors, $ix, 1); // rm the injector trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING); } @@ -16005,39 +19731,40 @@ public function execute($tokens, $config, $context) { // punt ($reprocess = true; continue;) and it does that for us. // isset is in loop because $tokens size changes during loop exec - for ( - $t = 0; - $t == 0 || isset($tokens[$t - 1]); - // only increment if we don't need to reprocess - $reprocess ? $reprocess = false : $t++ - ) { + for (;; + // only increment if we don't need to reprocess + $reprocess ? $reprocess = false : $token = $zipper->next($token)) { // check for a rewind - if (is_int($i) && $i >= 0) { + if (is_int($i)) { // possibility: disable rewinding if the current token has a // rewind set on it already. This would offer protection from // infinite loop, but might hinder some advanced rewinding. - $rewind_to = $this->injectors[$i]->getRewind(); - if (is_int($rewind_to) && $rewind_to < $t) { - if ($rewind_to < 0) $rewind_to = 0; - while ($t > $rewind_to) { - $t--; - $prev = $tokens[$t]; + $rewind_offset = $this->injectors[$i]->getRewindOffset(); + if (is_int($rewind_offset)) { + for ($j = 0; $j < $rewind_offset; $j++) { + if (empty($zipper->front)) break; + $token = $zipper->prev($token); // indicate that other injectors should not process this token, // but we need to reprocess it - unset($prev->skip[$i]); - $prev->rewind = $i; - if ($prev instanceof HTMLPurifier_Token_Start) array_pop($this->stack); - elseif ($prev instanceof HTMLPurifier_Token_End) $this->stack[] = $prev->start; + unset($token->skip[$i]); + $token->rewind = $i; + if ($token instanceof HTMLPurifier_Token_Start) { + array_pop($this->stack); + } elseif ($token instanceof HTMLPurifier_Token_End) { + $this->stack[] = $token->start; + } } } $i = false; } // handle case of document end - if (!isset($tokens[$t])) { + if ($token === NULL) { // kill processing if stack is empty - if (empty($this->stack)) break; + if (empty($this->stack)) { + break; + } // peek $top_nesting = array_pop($this->stack); @@ -16049,26 +19776,30 @@ public function execute($tokens, $config, $context) { } // append, don't splice, since this is the end - $tokens[] = new HTMLPurifier_Token_End($top_nesting->name); + $token = new HTMLPurifier_Token_End($top_nesting->name); // punt! $reprocess = true; continue; } - $token = $tokens[$t]; - - //echo '
      '; printTokens($tokens, $t); printTokens($this->stack); + //echo '
      '; printZipper($zipper, $token);//printTokens($this->stack); //flush(); // quick-check: if it's not a tag, no need to process if (empty($token->is_tag)) { if ($token instanceof HTMLPurifier_Token_Text) { foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) continue; - if ($token->rewind !== null && $token->rewind !== $i) continue; - $injector->handleText($token); - $this->processToken($token, $i); + if (isset($token->skip[$i])) { + continue; + } + if ($token->rewind !== null && $token->rewind !== $i) { + continue; + } + // XXX fuckup + $r = $token; + $injector->handleText($r); + $token = $this->processToken($r, $i); $reprocess = true; break; } @@ -16087,12 +19818,22 @@ public function execute($tokens, $config, $context) { $ok = false; if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) { // claims to be a start tag but is empty - $token = new HTMLPurifier_Token_Empty($token->name, $token->attr, $token->line, $token->col, $token->armor); + $token = new HTMLPurifier_Token_Empty( + $token->name, + $token->attr, + $token->line, + $token->col, + $token->armor + ); $ok = true; } elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) { // claims to be empty but really is a start tag - $this->swap(new HTMLPurifier_Token_End($token->name)); - $this->insertBefore(new HTMLPurifier_Token_Start($token->name, $token->attr, $token->line, $token->col, $token->armor)); + // NB: this assignment is required + $old_token = $token; + $token = new HTMLPurifier_Token_End($token->name); + $token = $this->insertBefore( + new HTMLPurifier_Token_Start($old_token->name, $old_token->attr, $old_token->line, $old_token->col, $old_token->armor) + ); // punt (since we had to modify the input stream in a non-trivial way) $reprocess = true; continue; @@ -16121,31 +19862,32 @@ public function execute($tokens, $config, $context) { $parent = array_pop($this->stack); $this->stack[] = $parent; + $parent_def = null; + $parent_elements = null; + $autoclose = false; if (isset($definition->info[$parent->name])) { - $elements = $definition->info[$parent->name]->child->getAllowedElements($config); - $autoclose = !isset($elements[$token->name]); - } else { - $autoclose = false; + $parent_def = $definition->info[$parent->name]; + $parent_elements = $parent_def->child->getAllowedElements($config); + $autoclose = !isset($parent_elements[$token->name]); } if ($autoclose && $definition->info[$token->name]->wrap) { - // Check if an element can be wrapped by another - // element to make it valid in a context (for + // Check if an element can be wrapped by another + // element to make it valid in a context (for // example,
          needs a
        • in between) $wrapname = $definition->info[$token->name]->wrap; $wrapdef = $definition->info[$wrapname]; $elements = $wrapdef->child->getAllowedElements($config); - $parent_elements = $definition->info[$parent->name]->child->getAllowedElements($config); if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) { $newtoken = new HTMLPurifier_Token_Start($wrapname); - $this->insertBefore($newtoken); + $token = $this->insertBefore($newtoken); $reprocess = true; continue; } } $carryover = false; - if ($autoclose && $definition->info[$parent->name]->formatting) { + if ($autoclose && $parent_def->formatting) { $carryover = true; } @@ -16175,15 +19917,6 @@ public function execute($tokens, $config, $context) { // errors need to be updated $new_token = new HTMLPurifier_Token_End($parent->name); $new_token->start = $parent; - if ($carryover) { - $element = clone $parent; - // [TagClosedAuto] - $element->armor['MakeWellFormed_TagClosedError'] = true; - $element->carryover = true; - $this->processToken(array($new_token, $token, $element)); - } else { - $this->insertBefore($new_token); - } // [TagClosedSuppress] if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) { if (!$carryover) { @@ -16192,8 +19925,17 @@ public function execute($tokens, $config, $context) { $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent); } } + if ($carryover) { + $element = clone $parent; + // [TagClosedAuto] + $element->armor['MakeWellFormed_TagClosedError'] = true; + $element->carryover = true; + $token = $this->processToken(array($new_token, $token, $element)); + } else { + $token = $this->insertBefore($new_token); + } } else { - $this->remove(); + $token = $this->remove(); } $reprocess = true; continue; @@ -16205,20 +19947,26 @@ public function execute($tokens, $config, $context) { if ($ok) { foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) continue; - if ($token->rewind !== null && $token->rewind !== $i) continue; - $injector->handleElement($token); - $this->processToken($token, $i); + if (isset($token->skip[$i])) { + continue; + } + if ($token->rewind !== null && $token->rewind !== $i) { + continue; + } + $r = $token; + $injector->handleElement($r); + $token = $this->processToken($r, $i); $reprocess = true; break; } if (!$reprocess) { // ah, nothing interesting happened; do normal processing - $this->swap($token); if ($token instanceof HTMLPurifier_Token_Start) { $this->stack[] = $token; } elseif ($token instanceof HTMLPurifier_Token_End) { - throw new HTMLPurifier_Exception('Improper handling of end tag in start code; possible error in MakeWellFormed'); + throw new HTMLPurifier_Exception( + 'Improper handling of end tag in start code; possible error in MakeWellFormed' + ); } } continue; @@ -16232,13 +19980,15 @@ public function execute($tokens, $config, $context) { // make sure that we have something open if (empty($this->stack)) { if ($escape_invalid_tags) { - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text'); - $this->swap(new HTMLPurifier_Token_Text( - $generator->generateFromToken($token) - )); + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text'); + } + $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token)); } else { - $this->remove(); - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed'); + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed'); + } + $token = $this->remove(); } $reprocess = true; continue; @@ -16252,10 +20002,15 @@ public function execute($tokens, $config, $context) { if ($current_parent->name == $token->name) { $token->start = $current_parent; foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) continue; - if ($token->rewind !== null && $token->rewind !== $i) continue; - $injector->handleEnd($token); - $this->processToken($token, $i); + if (isset($token->skip[$i])) { + continue; + } + if ($token->rewind !== null && $token->rewind !== $i) { + continue; + } + $r = $token; + $injector->handleEnd($r); + $token = $this->processToken($r, $i); $this->stack[] = $current_parent; $reprocess = true; break; @@ -16283,13 +20038,15 @@ public function execute($tokens, $config, $context) { // we didn't find the tag, so remove if ($skipped_tags === false) { if ($escape_invalid_tags) { - $this->swap(new HTMLPurifier_Token_Text( - $generator->generateFromToken($token) - )); - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text'); + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text'); + } + $token = new HTMLPurifier_Token_Text($generator->generateFromToken($token)); } else { - $this->remove(); - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed'); + if ($e) { + $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed'); + } + $token = $this->remove(); } $reprocess = true; continue; @@ -16322,18 +20079,17 @@ public function execute($tokens, $config, $context) { $replace[] = $element; } } - $this->processToken($replace); + $token = $this->processToken($replace); $reprocess = true; continue; } - $context->destroy('CurrentNesting'); - $context->destroy('InputTokens'); - $context->destroy('InputIndex'); $context->destroy('CurrentToken'); + $context->destroy('CurrentNesting'); + $context->destroy('InputZipper'); - unset($this->injectors, $this->stack, $this->tokens, $this->t); - return $tokens; + unset($this->injectors, $this->stack, $this->tokens); + return $zipper->toArray($token); } /** @@ -16352,25 +20108,38 @@ public function execute($tokens, $config, $context) { * If $token is an integer, that number of tokens (with the first token * being the current one) will be deleted. * - * @param $token Token substitution value - * @param $injector Injector that performed the substitution; default is if + * @param HTMLPurifier_Token|array|int|bool $token Token substitution value + * @param HTMLPurifier_Injector|int $injector Injector that performed the substitution; default is if * this is not an injector related operation. + * @throws HTMLPurifier_Exception */ - protected function processToken($token, $injector = -1) { - + protected function processToken($token, $injector = -1) + { // normalize forms of token - if (is_object($token)) $token = array(1, $token); - if (is_int($token)) $token = array($token); - if ($token === false) $token = array(1); - if (!is_array($token)) throw new HTMLPurifier_Exception('Invalid token type from injector'); - if (!is_int($token[0])) array_unshift($token, 1); - if ($token[0] === 0) throw new HTMLPurifier_Exception('Deleting zero tokens is not valid'); + if (is_object($token)) { + $token = array(1, $token); + } + if (is_int($token)) { + $token = array($token); + } + if ($token === false) { + $token = array(1); + } + if (!is_array($token)) { + throw new HTMLPurifier_Exception('Invalid token type from injector'); + } + if (!is_int($token[0])) { + array_unshift($token, 1); + } + if ($token[0] === 0) { + throw new HTMLPurifier_Exception('Deleting zero tokens is not valid'); + } // $token is now an array with the following form: // array(number nodes to delete, new node 1, new node 2, ...) $delete = array_shift($token); - $old = array_splice($this->tokens, $this->t, $delete, $token); + list($old, $r) = $this->zipper->splice($this->token, $delete, $token); if ($injector > -1) { // determine appropriate skips @@ -16381,32 +20150,32 @@ protected function processToken($token, $injector = -1) { } } + return $r; + } /** * Inserts a token before the current token. Cursor now points to * this token. You must reprocess after this. + * @param HTMLPurifier_Token $token */ - private function insertBefore($token) { - array_splice($this->tokens, $this->t, 0, array($token)); + private function insertBefore($token) + { + // NB not $this->zipper->insertBefore(), due to positioning + // differences + $splice = $this->zipper->splice($this->token, 0, array($token)); + + return $splice[1]; } /** * Removes current token. Cursor now points to new token occupying previously * occupied space. You must reprocess after this. */ - private function remove() { - array_splice($this->tokens, $this->t, 1); - } - - /** - * Swap current token with new token. Cursor points to new token (no - * change). You must reprocess after this. - */ - private function swap($token) { - $this->tokens[$this->t] = $token; + private function remove() + { + return $this->zipper->delete(); } - } @@ -16424,13 +20193,20 @@ private function swap($token) { class HTMLPurifier_Strategy_RemoveForeignElements extends HTMLPurifier_Strategy { - public function execute($tokens, $config, $context) { + /** + * @param HTMLPurifier_Token[] $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array|HTMLPurifier_Token[] + */ + public function execute($tokens, $config, $context) + { $definition = $config->getHTMLDefinition(); $generator = new HTMLPurifier_Generator($config, $context); $result = array(); $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); - $remove_invalid_img = $config->get('Core.RemoveInvalidImg'); + $remove_invalid_img = $config->get('Core.RemoveInvalidImg'); // currently only used to determine if comments should be kept $trusted = $config->get('HTML.Trusted'); @@ -16439,7 +20215,7 @@ public function execute($tokens, $config, $context) { $check_comments = $comment_lookup !== array() || $comment_regexp !== null; $remove_script_contents = $config->get('Core.RemoveScriptContents'); - $hidden_elements = $config->get('Core.HiddenElements'); + $hidden_elements = $config->get('Core.HiddenElements'); // remove script contents compatibility if ($remove_script_contents === true) { @@ -16464,34 +20240,31 @@ public function execute($tokens, $config, $context) { $e =& $context->get('ErrorCollector'); } - foreach($tokens as $token) { + foreach ($tokens as $token) { if ($remove_until) { if (empty($token->is_tag) || $token->name !== $remove_until) { continue; } } - if (!empty( $token->is_tag )) { + if (!empty($token->is_tag)) { // DEFINITION CALL // before any processing, try to transform the element - if ( - isset($definition->info_tag_transform[$token->name]) - ) { + if (isset($definition->info_tag_transform[$token->name])) { $original_name = $token->name; // there is a transformation for this tag // DEFINITION CALL $token = $definition-> - info_tag_transform[$token->name]-> - transform($token, $config, $context); - if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name); + info_tag_transform[$token->name]->transform($token, $config, $context); + if ($e) { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name); + } } if (isset($definition->info[$token->name])) { - // mostly everything's good, but // we need to make sure required attributes are in order - if ( - ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) && + if (($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) && $definition->info[$token->name]->required_attr && ($token->name != 'img' || $remove_invalid_img) // ensure config option still works ) { @@ -16504,7 +20277,13 @@ public function execute($tokens, $config, $context) { } } if (!$ok) { - if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Missing required attribute', $name); + if ($e) { + $e->send( + E_ERROR, + 'Strategy_RemoveForeignElements: Missing required attribute', + $name + ); + } continue; } $token->armor['ValidateAttributes'] = true; @@ -16518,7 +20297,9 @@ public function execute($tokens, $config, $context) { } elseif ($escape_invalid_tags) { // invalid tag, generate HTML representation and insert in - if ($e) $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text'); + if ($e) { + $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text'); + } $token = new HTMLPurifier_Token_Text( $generator->generateFromToken($token) ); @@ -16533,9 +20314,13 @@ public function execute($tokens, $config, $context) { } else { $remove_until = false; } - if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed'); + if ($e) { + $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed'); + } } else { - if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed'); + if ($e) { + $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed'); + } } continue; } @@ -16559,11 +20344,15 @@ public function execute($tokens, $config, $context) { $found_double_hyphen = true; $token->data = str_replace('--', '-', $token->data); } - if ($trusted || !empty($comment_lookup[trim($token->data)]) || ($comment_regexp !== NULL && preg_match($comment_regexp, trim($token->data)))) { + if ($trusted || !empty($comment_lookup[trim($token->data)]) || + ($comment_regexp !== null && preg_match($comment_regexp, trim($token->data)))) { // OK good if ($e) { if ($trailing_hyphen) { - $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed'); + $e->send( + E_NOTICE, + 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' + ); } if ($found_double_hyphen) { $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed'); @@ -16577,7 +20366,9 @@ public function execute($tokens, $config, $context) { } } else { // strip comments - if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed'); + if ($e) { + $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed'); + } continue; } } elseif ($token instanceof HTMLPurifier_Token_Text) { @@ -16590,12 +20381,9 @@ public function execute($tokens, $config, $context) { // we removed tokens until the end, throw error $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until); } - $context->destroy('CurrentToken'); - return $result; } - } @@ -16609,8 +20397,14 @@ public function execute($tokens, $config, $context) { class HTMLPurifier_Strategy_ValidateAttributes extends HTMLPurifier_Strategy { - public function execute($tokens, $config, $context) { - + /** + * @param HTMLPurifier_Token[] $tokens + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_Token[] + */ + public function execute($tokens, $config, $context) + { // setup validator $validator = new HTMLPurifier_AttrValidator(); @@ -16621,21 +20415,21 @@ public function execute($tokens, $config, $context) { // only process tokens that have attributes, // namely start and empty tags - if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) continue; + if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) { + continue; + } // skip tokens that are armored - if (!empty($token->armor['ValidateAttributes'])) continue; + if (!empty($token->armor['ValidateAttributes'])) { + continue; + } // note that we have no facilities here for removing tokens $validator->validateToken($token, $config, $context); - - $tokens[$key] = $token; // for PHP 4 } $context->destroy('CurrentToken'); - return $tokens; } - } @@ -16659,9 +20453,14 @@ public function execute($tokens, $config, $context) { */ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform { - + /** + * @type string + */ public $transform_to = 'span'; + /** + * @type array + */ protected $_size_lookup = array( '0' => 'xx-small', '1' => 'xx-small', @@ -16679,8 +20478,14 @@ class HTMLPurifier_TagTransform_Font extends HTMLPurifier_TagTransform '+4' => '300%' ); - public function transform($tag, $config, $context) { - + /** + * @param HTMLPurifier_Token_Tag $tag + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return HTMLPurifier_Token_End|string + */ + public function transform($tag, $config, $context) + { if ($tag instanceof HTMLPurifier_Token_End) { $new_tag = clone $tag; $new_tag->name = $this->transform_to; @@ -16707,17 +20512,23 @@ public function transform($tag, $config, $context) { // normalize large numbers if ($attr['size'] !== '') { if ($attr['size']{0} == '+' || $attr['size']{0} == '-') { - $size = (int) $attr['size']; - if ($size < -2) $attr['size'] = '-2'; - if ($size > 4) $attr['size'] = '+4'; + $size = (int)$attr['size']; + if ($size < -2) { + $attr['size'] = '-2'; + } + if ($size > 4) { + $attr['size'] = '+4'; + } } else { - $size = (int) $attr['size']; - if ($size > 7) $attr['size'] = '7'; + $size = (int)$attr['size']; + if ($size > 7) { + $attr['size'] = '7'; + } } } if (isset($this->_size_lookup[$attr['size']])) { $prepend_style .= 'font-size:' . - $this->_size_lookup[$attr['size']] . ';'; + $this->_size_lookup[$attr['size']] . ';'; } unset($attr['size']); } @@ -16733,7 +20544,6 @@ public function transform($tag, $config, $context) { $new_tag->attr = $attr; return $new_tag; - } } @@ -16748,19 +20558,29 @@ public function transform($tag, $config, $context) { */ class HTMLPurifier_TagTransform_Simple extends HTMLPurifier_TagTransform { - + /** + * @type string + */ protected $style; /** - * @param $transform_to Tag name to transform to. - * @param $style CSS style to add to the tag + * @param string $transform_to Tag name to transform to. + * @param string $style CSS style to add to the tag */ - public function __construct($transform_to, $style = null) { + public function __construct($transform_to, $style = null) + { $this->transform_to = $transform_to; $this->style = $style; } - public function transform($tag, $config, $context) { + /** + * @param HTMLPurifier_Token_Tag $tag + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return string + */ + public function transform($tag, $config, $context) + { $new_tag = clone $tag; $new_tag->name = $this->transform_to; if (!is_null($this->style) && @@ -16770,7 +20590,6 @@ public function transform($tag, $config, $context) { } return $new_tag; } - } @@ -16782,17 +20601,33 @@ public function transform($tag, $config, $context) { */ class HTMLPurifier_Token_Comment extends HTMLPurifier_Token { - public $data; /**< Character data within comment. */ + /** + * Character data within comment. + * @type string + */ + public $data; + + /** + * @type bool + */ public $is_whitespace = true; + /** * Transparent constructor. * - * @param $data String comment data. + * @param string $data String comment data. + * @param int $line + * @param int $col */ - public function __construct($data, $line = null, $col = null) { + public function __construct($data, $line = null, $col = null) + { $this->data = $data; $this->line = $line; - $this->col = $col; + $this->col = $col; + } + + public function toNode() { + return new HTMLPurifier_Node_Comment($this->data, $this->line, $this->col); } } @@ -16803,13 +20638,14 @@ public function __construct($data, $line = null, $col = null) { /** * Abstract class of a tag token (start, end or empty), and its behavior. */ -class HTMLPurifier_Token_Tag extends HTMLPurifier_Token +abstract class HTMLPurifier_Token_Tag extends HTMLPurifier_Token { /** * Static bool marker that indicates the class is a tag. * * This allows us to check objects with !empty($obj->is_tag) * without having to use a function call is_a(). + * @type bool */ public $is_tag = true; @@ -16819,21 +20655,27 @@ class HTMLPurifier_Token_Tag extends HTMLPurifier_Token * @note Strictly speaking, XML tags are case sensitive, so we shouldn't * be lower-casing them, but these tokens cater to HTML tags, which are * insensitive. + * @type string */ public $name; /** * Associative array of the tag's attributes. + * @type array */ public $attr = array(); /** * Non-overloaded constructor, which lower-cases passed tag name. * - * @param $name String name. - * @param $attr Associative array of attributes. - */ - public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) { + * @param string $name String name. + * @param array $attr Associative array of attributes. + * @param int $line + * @param int $col + * @param array $armor + */ + public function __construct($name, $attr = array(), $line = null, $col = null, $armor = array()) + { $this->name = ctype_lower($name) ? $name : strtolower($name); foreach ($attr as $key => $value) { // normalization only necessary when key is not lowercase @@ -16849,9 +20691,13 @@ public function __construct($name, $attr = array(), $line = null, $col = null, $ } $this->attr = $attr; $this->line = $line; - $this->col = $col; + $this->col = $col; $this->armor = $armor; } + + public function toNode() { + return new HTMLPurifier_Node_Element($this->name, $this->attr, $this->line, $this->col, $this->armor); + } } @@ -16863,7 +20709,11 @@ public function __construct($name, $attr = array(), $line = null, $col = null, $ */ class HTMLPurifier_Token_Empty extends HTMLPurifier_Token_Tag { - + public function toNode() { + $n = parent::toNode(); + $n->empty = true; + return $n; + } } @@ -16880,10 +20730,15 @@ class HTMLPurifier_Token_Empty extends HTMLPurifier_Token_Tag class HTMLPurifier_Token_End extends HTMLPurifier_Token_Tag { /** - * Token that started this node. Added by MakeWellFormed. Please - * do not edit this! + * Token that started this node. + * Added by MakeWellFormed. Please do not edit this! + * @type HTMLPurifier_Token */ public $start; + + public function toNode() { + throw new Exception("HTMLPurifier_Token_End->toNode not supported!"); + } } @@ -16895,7 +20750,6 @@ class HTMLPurifier_Token_End extends HTMLPurifier_Token_Tag */ class HTMLPurifier_Token_Start extends HTMLPurifier_Token_Tag { - } @@ -16914,22 +20768,42 @@ class HTMLPurifier_Token_Start extends HTMLPurifier_Token_Tag class HTMLPurifier_Token_Text extends HTMLPurifier_Token { - public $name = '#PCDATA'; /**< PCDATA tag name compatible with DTD. */ - public $data; /**< Parsed character data of text. */ - public $is_whitespace; /**< Bool indicating if node is whitespace. */ + /** + * @type string + */ + public $name = '#PCDATA'; + /**< PCDATA tag name compatible with DTD. */ + + /** + * @type string + */ + public $data; + /**< Parsed character data of text. */ + + /** + * @type bool + */ + public $is_whitespace; + + /**< Bool indicating if node is whitespace. */ /** * Constructor, accepts data and determines if it is whitespace. - * - * @param $data String parsed character data. + * @param string $data String parsed character data. + * @param int $line + * @param int $col */ - public function __construct($data, $line = null, $col = null) { + public function __construct($data, $line = null, $col = null) + { $this->data = $data; $this->is_whitespace = ctype_space($data); $this->line = $line; - $this->col = $col; + $this->col = $col; } + public function toNode() { + return new HTMLPurifier_Node_Text($this->data, $this->is_whitespace, $this->line, $this->col); + } } @@ -16938,19 +20812,50 @@ public function __construct($data, $line = null, $col = null) { class HTMLPurifier_URIFilter_DisableExternal extends HTMLPurifier_URIFilter { + /** + * @type string + */ public $name = 'DisableExternal'; + + /** + * @type array + */ protected $ourHostParts = false; - public function prepare($config) { + + /** + * @param HTMLPurifier_Config $config + * @return void + */ + public function prepare($config) + { $our_host = $config->getDefinition('URI')->host; - if ($our_host !== null) $this->ourHostParts = array_reverse(explode('.', $our_host)); + if ($our_host !== null) { + $this->ourHostParts = array_reverse(explode('.', $our_host)); + } } - public function filter(&$uri, $config, $context) { - if (is_null($uri->host)) return true; - if ($this->ourHostParts === false) return false; + + /** + * @param HTMLPurifier_URI $uri Reference + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + if (is_null($uri->host)) { + return true; + } + if ($this->ourHostParts === false) { + return false; + } $host_parts = array_reverse(explode('.', $uri->host)); foreach ($this->ourHostParts as $i => $x) { - if (!isset($host_parts[$i])) return false; - if ($host_parts[$i] != $this->ourHostParts[$i]) return false; + if (!isset($host_parts[$i])) { + return false; + } + if ($host_parts[$i] != $this->ourHostParts[$i]) { + return false; + } } return true; } @@ -16962,9 +20867,22 @@ public function filter(&$uri, $config, $context) { class HTMLPurifier_URIFilter_DisableExternalResources extends HTMLPurifier_URIFilter_DisableExternal { + /** + * @type string + */ public $name = 'DisableExternalResources'; - public function filter(&$uri, $config, $context) { - if (!$context->get('EmbeddedURI', true)) return true; + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + if (!$context->get('EmbeddedURI', true)) { + return true; + } return parent::filter($uri, $config, $context); } } @@ -16975,8 +20893,19 @@ public function filter(&$uri, $config, $context) { class HTMLPurifier_URIFilter_DisableResources extends HTMLPurifier_URIFilter { + /** + * @type string + */ public $name = 'DisableResources'; - public function filter(&$uri, $config, $context) { + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { return !$context->get('EmbeddedURI', true); } } @@ -16991,14 +20920,35 @@ public function filter(&$uri, $config, $context) { // points are involved), but I'm not 100% sure class HTMLPurifier_URIFilter_HostBlacklist extends HTMLPurifier_URIFilter { + /** + * @type string + */ public $name = 'HostBlacklist'; + + /** + * @type array + */ protected $blacklist = array(); - public function prepare($config) { + + /** + * @param HTMLPurifier_Config $config + * @return bool + */ + public function prepare($config) + { $this->blacklist = $config->get('URI.HostBlacklist'); return true; } - public function filter(&$uri, $config, $context) { - foreach($this->blacklist as $blacklisted_host_fragment) { + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + foreach ($this->blacklist as $blacklisted_host_fragment) { if (strpos($uri->host, $blacklisted_host_fragment) !== false) { return false; } @@ -17015,14 +20965,35 @@ public function filter(&$uri, $config, $context) { class HTMLPurifier_URIFilter_MakeAbsolute extends HTMLPurifier_URIFilter { + /** + * @type string + */ public $name = 'MakeAbsolute'; + + /** + * @type + */ protected $base; + + /** + * @type array + */ protected $basePathStack = array(); - public function prepare($config) { + + /** + * @param HTMLPurifier_Config $config + * @return bool + */ + public function prepare($config) + { $def = $config->getDefinition('URI'); $this->base = $def->base; if (is_null($this->base)) { - trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_WARNING); + trigger_error( + 'URI.MakeAbsolute is being ignored due to lack of ' . + 'value for URI.Base configuration', + E_USER_WARNING + ); return false; } $this->base->fragment = null; // fragment is invalid for base URI @@ -17032,19 +21003,29 @@ public function prepare($config) { $this->basePathStack = $stack; return true; } - public function filter(&$uri, $config, $context) { - if (is_null($this->base)) return true; // abort early - if ( - $uri->path === '' && is_null($uri->scheme) && - is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment) - ) { + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + if (is_null($this->base)) { + return true; + } // abort early + if ($uri->path === '' && is_null($uri->scheme) && + is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment)) { // reference to current document $uri = clone $this->base; return true; } if (!is_null($uri->scheme)) { // absolute URI already: don't change - if (!is_null($uri->host)) return true; + if (!is_null($uri->host)) { + return true; + } $scheme_obj = $uri->getSchemeObj($config, $context); if (!$scheme_obj) { // scheme not recognized @@ -17077,22 +21058,33 @@ public function filter(&$uri, $config, $context) { } // re-combine $uri->scheme = $this->base->scheme; - if (is_null($uri->userinfo)) $uri->userinfo = $this->base->userinfo; - if (is_null($uri->host)) $uri->host = $this->base->host; - if (is_null($uri->port)) $uri->port = $this->base->port; + if (is_null($uri->userinfo)) { + $uri->userinfo = $this->base->userinfo; + } + if (is_null($uri->host)) { + $uri->host = $this->base->host; + } + if (is_null($uri->port)) { + $uri->port = $this->base->port; + } return true; } /** * Resolve dots and double-dots in a path stack + * @param array $stack + * @return array */ - private function _collapseStack($stack) { + private function _collapseStack($stack) + { $result = array(); $is_folder = false; for ($i = 0; isset($stack[$i]); $i++) { $is_folder = false; // absorb an internally duplicated slash - if ($stack[$i] == '' && $i && isset($stack[$i+1])) continue; + if ($stack[$i] == '' && $i && isset($stack[$i + 1])) { + continue; + } if ($stack[$i] == '..') { if (!empty($result)) { $segment = array_pop($result); @@ -17117,7 +21109,9 @@ private function _collapseStack($stack) { } $result[] = $stack[$i]; } - if ($is_folder) $result[] = ''; + if ($is_folder) { + $result[] = ''; + } return $result; } } @@ -17128,26 +21122,79 @@ private function _collapseStack($stack) { class HTMLPurifier_URIFilter_Munge extends HTMLPurifier_URIFilter { + /** + * @type string + */ public $name = 'Munge'; + + /** + * @type bool + */ public $post = true; - private $target, $parser, $doEmbed, $secretKey; + /** + * @type string + */ + private $target; + + /** + * @type HTMLPurifier_URIParser + */ + private $parser; + + /** + * @type bool + */ + private $doEmbed; + + /** + * @type string + */ + private $secretKey; + + /** + * @type array + */ protected $replace = array(); - public function prepare($config) { - $this->target = $config->get('URI.' . $this->name); - $this->parser = new HTMLPurifier_URIParser(); - $this->doEmbed = $config->get('URI.MungeResources'); + /** + * @param HTMLPurifier_Config $config + * @return bool + */ + public function prepare($config) + { + $this->target = $config->get('URI.' . $this->name); + $this->parser = new HTMLPurifier_URIParser(); + $this->doEmbed = $config->get('URI.MungeResources'); $this->secretKey = $config->get('URI.MungeSecretKey'); + if ($this->secretKey && !function_exists('hash_hmac')) { + throw new Exception("Cannot use %URI.MungeSecretKey without hash_hmac support."); + } return true; } - public function filter(&$uri, $config, $context) { - if ($context->get('EmbeddedURI', true) && !$this->doEmbed) return true; + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { + if ($context->get('EmbeddedURI', true) && !$this->doEmbed) { + return true; + } $scheme_obj = $uri->getSchemeObj($config, $context); - if (!$scheme_obj) return true; // ignore unknown schemes, maybe another postfilter did it - if (!$scheme_obj->browsable) return true; // ignore non-browseable schemes, since we can't munge those in a reasonable way - if ($uri->isBenign($config, $context)) return true; // don't redirect if a benign URL + if (!$scheme_obj) { + return true; + } // ignore unknown schemes, maybe another postfilter did it + if (!$scheme_obj->browsable) { + return true; + } // ignore non-browseable schemes, since we can't munge those in a reasonable way + if ($uri->isBenign($config, $context)) { + return true; + } // don't redirect if a benign URL $this->makeReplace($uri, $config, $context); $this->replace = array_map('rawurlencode', $this->replace); @@ -17156,12 +21203,20 @@ public function filter(&$uri, $config, $context) { $new_uri = $this->parser->parse($new_uri); // don't redirect if the target host is the same as the // starting host - if ($uri->host === $new_uri->host) return true; + if ($uri->host === $new_uri->host) { + return true; + } $uri = $new_uri; // overwrite return true; } - protected function makeReplace($uri, $config, $context) { + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + */ + protected function makeReplace($uri, $config, $context) + { $string = $uri->toString(); // always available $this->replace['%s'] = $string; @@ -17171,9 +21226,10 @@ protected function makeReplace($uri, $config, $context) { $this->replace['%m'] = $context->get('CurrentAttr', true); $this->replace['%p'] = $context->get('CurrentCSSProperty', true); // not always available - if ($this->secretKey) $this->replace['%t'] = sha1($this->secretKey . ':' . $string); + if ($this->secretKey) { + $this->replace['%t'] = hash_hmac("sha256", $string, $this->secretKey); + } } - } @@ -17188,25 +21244,58 @@ protected function makeReplace($uri, $config, $context) { */ class HTMLPurifier_URIFilter_SafeIframe extends HTMLPurifier_URIFilter { + /** + * @type string + */ public $name = 'SafeIframe'; + + /** + * @type bool + */ public $always_load = true; - protected $regexp = NULL; - // XXX: The not so good bit about how this is all setup now is we + + /** + * @type string + */ + protected $regexp = null; + + // XXX: The not so good bit about how this is all set up now is we // can't check HTML.SafeIframe in the 'prepare' step: we have to // defer till the actual filtering. - public function prepare($config) { + /** + * @param HTMLPurifier_Config $config + * @return bool + */ + public function prepare($config) + { $this->regexp = $config->get('URI.SafeIframeRegexp'); return true; } - public function filter(&$uri, $config, $context) { + + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function filter(&$uri, $config, $context) + { // check if filter not applicable - if (!$config->get('HTML.SafeIframe')) return true; + if (!$config->get('HTML.SafeIframe')) { + return true; + } // check if the filter should actually trigger - if (!$context->get('EmbeddedURI', true)) return true; + if (!$context->get('EmbeddedURI', true)) { + return true; + } $token = $context->get('CurrentToken', true); - if (!($token && $token->name == 'iframe')) return true; + if (!($token && $token->name == 'iframe')) { + return true; + } // check if we actually have some whitelists enabled - if ($this->regexp === null) return false; + if ($this->regexp === null) { + return false; + } // actually check the whitelists return preg_match($this->regexp, $uri->toString()); } @@ -17219,21 +21308,38 @@ public function filter(&$uri, $config, $context) { /** * Implements data: URI for base64 encoded images supported by GD. */ -class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme { - +class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme +{ + /** + * @type bool + */ public $browsable = true; + + /** + * @type array + */ public $allowed_types = array( // you better write validation code for other types if you // decide to allow them 'image/jpeg' => true, 'image/gif' => true, 'image/png' => true, - ); + ); // this is actually irrelevant since we only write out the path // component + /** + * @type bool + */ public $may_omit_host = true; - public function doValidate(&$uri, $config, $context) { + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { $result = explode(',', $uri->path, 2); $is_base64 = false; $charset = null; @@ -17242,7 +21348,7 @@ public function doValidate(&$uri, $config, $context) { list($metadata, $data) = $result; // do some legwork on the metadata $metas = explode(';', $metadata); - while(!empty($metas)) { + while (!empty($metas)) { $cur = array_shift($metas); if ($cur == 'base64') { $is_base64 = true; @@ -17251,10 +21357,14 @@ public function doValidate(&$uri, $config, $context) { if (substr($cur, 0, 8) == 'charset=') { // doesn't match if there are arbitrary spaces, but // whatever dude - if ($charset !== null) continue; // garbage + if ($charset !== null) { + continue; + } // garbage $charset = substr($cur, 8); // not used } else { - if ($content_type !== null) continue; // garbage + if ($content_type !== null) { + continue; + } // garbage $content_type = $cur; } } @@ -17286,7 +21396,9 @@ public function doValidate(&$uri, $config, $context) { $info = getimagesize($file); restore_error_handler(); unlink($file); - if ($info == false) return false; + if ($info == false) { + return false; + } $image_code = $info[2]; } else { trigger_error("could not find exif_imagetype or getimagesize functions", E_USER_ERROR); @@ -17295,7 +21407,9 @@ public function doValidate(&$uri, $config, $context) { if ($real_content_type != $content_type) { // we're nice guys; if the content type is something else we // support, change it over - if (empty($this->allowed_types[$real_content_type])) return false; + if (empty($this->allowed_types[$real_content_type])) { + return false; + } $content_type = $real_content_type; } // ok, it's kosher, rewrite what we need @@ -17308,40 +21422,56 @@ public function doValidate(&$uri, $config, $context) { return true; } - public function muteErrorHandler($errno, $errstr) {} - + /** + * @param int $errno + * @param string $errstr + */ + public function muteErrorHandler($errno, $errstr) + { + } } - /** * Validates file as defined by RFC 1630 and RFC 1738. */ -class HTMLPurifier_URIScheme_file extends HTMLPurifier_URIScheme { - - // Generally file:// URLs are not accessible from most - // machines, so placing them as an img src is incorrect. +class HTMLPurifier_URIScheme_file extends HTMLPurifier_URIScheme +{ + /** + * Generally file:// URLs are not accessible from most + * machines, so placing them as an img src is incorrect. + * @type bool + */ public $browsable = false; - // Basically the *only* URI scheme for which this is true, since - // accessing files on the local machine is very common. In fact, - // browsers on some operating systems don't understand the - // authority, though I hear it is used on Windows to refer to - // network shares. + /** + * Basically the *only* URI scheme for which this is true, since + * accessing files on the local machine is very common. In fact, + * browsers on some operating systems don't understand the + * authority, though I hear it is used on Windows to refer to + * network shares. + * @type bool + */ public $may_omit_host = true; - public function doValidate(&$uri, $config, $context) { + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { // Authentication method is not supported $uri->userinfo = null; // file:// makes no provisions for accessing the resource - $uri->port = null; + $uri->port = null; // While it seems to work on Firefox, the querystring has // no possible effect and is thus stripped. - $uri->query = null; + $uri->query = null; return true; } - } @@ -17351,14 +21481,32 @@ public function doValidate(&$uri, $config, $context) { /** * Validates ftp (File Transfer Protocol) URIs as defined by generic RFC 1738. */ -class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme { - +class HTMLPurifier_URIScheme_ftp extends HTMLPurifier_URIScheme +{ + /** + * @type int + */ public $default_port = 21; + + /** + * @type bool + */ public $browsable = true; // usually + + /** + * @type bool + */ public $hierarchical = true; - public function doValidate(&$uri, $config, $context) { - $uri->query = null; + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { + $uri->query = null; // typecode check $semicolon_pos = strrpos($uri->path, ';'); // reverse @@ -17381,10 +21529,8 @@ public function doValidate(&$uri, $config, $context) { $uri->path = str_replace(';', '%3B', $uri->path); $uri->path .= $type_ret; } - return true; } - } @@ -17394,17 +21540,34 @@ public function doValidate(&$uri, $config, $context) { /** * Validates http (HyperText Transfer Protocol) as defined by RFC 2616 */ -class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme { - +class HTMLPurifier_URIScheme_http extends HTMLPurifier_URIScheme +{ + /** + * @type int + */ public $default_port = 80; + + /** + * @type bool + */ public $browsable = true; + + /** + * @type bool + */ public $hierarchical = true; - public function doValidate(&$uri, $config, $context) { + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { $uri->userinfo = null; return true; } - } @@ -17414,11 +21577,16 @@ public function doValidate(&$uri, $config, $context) { /** * Validates https (Secure HTTP) according to http scheme. */ -class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http { - +class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http +{ + /** + * @type int + */ public $default_port = 443; + /** + * @type bool + */ public $secure = true; - } @@ -17434,19 +21602,32 @@ class HTMLPurifier_URIScheme_https extends HTMLPurifier_URIScheme_http { * @todo Filter allowed query parameters */ -class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme { - +class HTMLPurifier_URIScheme_mailto extends HTMLPurifier_URIScheme +{ + /** + * @type bool + */ public $browsable = false; + + /** + * @type bool + */ public $may_omit_host = true; - public function doValidate(&$uri, $config, $context) { + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { $uri->userinfo = null; $uri->host = null; $uri->port = null; // we need to validate path against RFC 2368's addr-spec return true; } - } @@ -17456,20 +21637,33 @@ public function doValidate(&$uri, $config, $context) { /** * Validates news (Usenet) as defined by generic RFC 1738 */ -class HTMLPurifier_URIScheme_news extends HTMLPurifier_URIScheme { - +class HTMLPurifier_URIScheme_news extends HTMLPurifier_URIScheme +{ + /** + * @type bool + */ public $browsable = false; + + /** + * @type bool + */ public $may_omit_host = true; - public function doValidate(&$uri, $config, $context) { + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { $uri->userinfo = null; - $uri->host = null; - $uri->port = null; - $uri->query = null; + $uri->host = null; + $uri->port = null; + $uri->query = null; // typecode check needed on path return true; } - } @@ -17479,17 +21673,30 @@ public function doValidate(&$uri, $config, $context) { /** * Validates nntp (Network News Transfer Protocol) as defined by generic RFC 1738 */ -class HTMLPurifier_URIScheme_nntp extends HTMLPurifier_URIScheme { - +class HTMLPurifier_URIScheme_nntp extends HTMLPurifier_URIScheme +{ + /** + * @type int + */ public $default_port = 119; + + /** + * @type bool + */ public $browsable = false; - public function doValidate(&$uri, $config, $context) { + /** + * @param HTMLPurifier_URI $uri + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return bool + */ + public function doValidate(&$uri, $config, $context) + { $uri->userinfo = null; - $uri->query = null; + $uri->query = null; return true; } - } @@ -17503,28 +21710,41 @@ public function doValidate(&$uri, $config, $context) { */ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser { - - protected function parseImplementation($var, $type, $allow_null) { - if ($allow_null && $var === null) return null; + /** + * @param mixed $var + * @param int $type + * @param bool $allow_null + * @return array|bool|float|int|mixed|null|string + * @throws HTMLPurifier_VarParserException + */ + protected function parseImplementation($var, $type, $allow_null) + { + if ($allow_null && $var === null) { + return null; + } switch ($type) { // Note: if code "breaks" from the switch, it triggers a generic // exception to be thrown. Specific errors can be specifically // done here. - case self::MIXED : - case self::ISTRING : - case self::STRING : - case self::TEXT : - case self::ITEXT : + case self::MIXED: + case self::ISTRING: + case self::STRING: + case self::TEXT: + case self::ITEXT: return $var; - case self::INT : - if (is_string($var) && ctype_digit($var)) $var = (int) $var; + case self::INT: + if (is_string($var) && ctype_digit($var)) { + $var = (int)$var; + } return $var; - case self::FLOAT : - if ((is_string($var) && is_numeric($var)) || is_int($var)) $var = (float) $var; + case self::FLOAT: + if ((is_string($var) && is_numeric($var)) || is_int($var)) { + $var = (float)$var; + } return $var; - case self::BOOL : + case self::BOOL: if (is_int($var) && ($var === 0 || $var === 1)) { - $var = (bool) $var; + $var = (bool)$var; } elseif (is_string($var)) { if ($var == 'on' || $var == 'true' || $var == '1') { $var = true; @@ -17535,45 +21755,56 @@ protected function parseImplementation($var, $type, $allow_null) { } } return $var; - case self::ALIST : - case self::HASH : - case self::LOOKUP : + case self::ALIST: + case self::HASH: + case self::LOOKUP: if (is_string($var)) { // special case: technically, this is an array with // a single empty string item, but having an empty // array is more intuitive - if ($var == '') return array(); + if ($var == '') { + return array(); + } if (strpos($var, "\n") === false && strpos($var, "\r") === false) { // simplistic string to array method that only works // for simple lists of tag names or alphanumeric characters - $var = explode(',',$var); + $var = explode(',', $var); } else { $var = preg_split('/(,|[\n\r]+)/', $var); } // remove spaces - foreach ($var as $i => $j) $var[$i] = trim($j); + foreach ($var as $i => $j) { + $var[$i] = trim($j); + } if ($type === self::HASH) { // key:value,key2:value2 $nvar = array(); foreach ($var as $keypair) { $c = explode(':', $keypair, 2); - if (!isset($c[1])) continue; + if (!isset($c[1])) { + continue; + } $nvar[trim($c[0])] = trim($c[1]); } $var = $nvar; } } - if (!is_array($var)) break; + if (!is_array($var)) { + break; + } $keys = array_keys($var); if ($keys === array_keys($keys)) { - if ($type == self::ALIST) return $var; - elseif ($type == self::LOOKUP) { + if ($type == self::ALIST) { + return $var; + } elseif ($type == self::LOOKUP) { $new = array(); foreach ($var as $key) { $new[$key] = true; } return $new; - } else break; + } else { + break; + } } if ($type === self::ALIST) { trigger_error("Array list did not have consecutive integer indexes", E_USER_WARNING); @@ -17582,7 +21813,11 @@ protected function parseImplementation($var, $type, $allow_null) { if ($type === self::LOOKUP) { foreach ($var as $key => $value) { if ($value !== true) { - trigger_error("Lookup array has non-true value at key '$key'; maybe your input array was not indexed numerically", E_USER_WARNING); + trigger_error( + "Lookup array has non-true value at key '$key'; " . + "maybe your input array was not indexed numerically", + E_USER_WARNING + ); } $var[$key] = true; } @@ -17593,7 +21828,6 @@ protected function parseImplementation($var, $type, $allow_null) { } $this->errorGeneric($var, $type); } - } @@ -17608,11 +21842,24 @@ protected function parseImplementation($var, $type, $allow_null) { class HTMLPurifier_VarParser_Native extends HTMLPurifier_VarParser { - protected function parseImplementation($var, $type, $allow_null) { + /** + * @param mixed $var + * @param int $type + * @param bool $allow_null + * @return null|string + */ + protected function parseImplementation($var, $type, $allow_null) + { return $this->evalExpression($var); } - protected function evalExpression($expr) { + /** + * @param string $expr + * @return mixed + * @throws HTMLPurifier_VarParserException + */ + protected function evalExpression($expr) + { $var = null; $result = eval("\$var = $expr;"); if ($result === false) { @@ -17620,7 +21867,6 @@ protected function evalExpression($expr) { } return $var; } - } diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/Interchange.php b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/Interchange.php index ca4e4b68dc3..c094fa0b83c 100644 --- a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/Interchange.php +++ b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/Interchange.php @@ -10,18 +10,23 @@ class HTMLPurifier_ConfigSchema_Interchange /** * Name of the application this schema is describing. + * @type string */ public $name; /** * Array of Directive ID => array(directive info) + * @type HTMLPurifier_ConfigSchema_Interchange_Directive[] */ public $directives = array(); /** * Adds a directive array to $directives + * @param HTMLPurifier_ConfigSchema_Interchange_Directive $directive + * @throws HTMLPurifier_ConfigSchema_Exception */ - public function addDirective($directive) { + public function addDirective($directive) + { if (isset($this->directives[$i = $directive->id->toString()])) { throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine directive '$i'"); } @@ -32,11 +37,11 @@ public function addDirective($directive) { * Convenience function to perform standard validation. Throws exception * on failed validation. */ - public function validate() { + public function validate() + { $validator = new HTMLPurifier_ConfigSchema_Validator(); return $validator->validate($this); } - } // vim: et sw=4 sts=4 diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/InterchangeBuilder.php b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/InterchangeBuilder.php index 3d499e52363..fe9b3268f45 100644 --- a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/InterchangeBuilder.php +++ b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/InterchangeBuilder.php @@ -5,21 +5,39 @@ class HTMLPurifier_ConfigSchema_InterchangeBuilder /** * Used for processing DEFAULT, nothing else. + * @type HTMLPurifier_VarParser */ protected $varParser; - public function __construct($varParser = null) { + /** + * @param HTMLPurifier_VarParser $varParser + */ + public function __construct($varParser = null) + { $this->varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native(); } - public static function buildFromDirectory($dir = null) { - $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder(); + /** + * @param string $dir + * @return HTMLPurifier_ConfigSchema_Interchange + */ + public static function buildFromDirectory($dir = null) + { + $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder(); $interchange = new HTMLPurifier_ConfigSchema_Interchange(); return $builder->buildDir($interchange, $dir); } - public function buildDir($interchange, $dir = null) { - if (!$dir) $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema'; + /** + * @param HTMLPurifier_ConfigSchema_Interchange $interchange + * @param string $dir + * @return HTMLPurifier_ConfigSchema_Interchange + */ + public function buildDir($interchange, $dir = null) + { + if (!$dir) { + $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema'; + } if (file_exists($dir . '/info.ini')) { $info = parse_ini_file($dir . '/info.ini'); $interchange->name = $info['name']; @@ -39,24 +57,30 @@ public function buildDir($interchange, $dir = null) { foreach ($files as $file) { $this->buildFile($interchange, $dir . '/' . $file); } - return $interchange; } - public function buildFile($interchange, $file) { + /** + * @param HTMLPurifier_ConfigSchema_Interchange $interchange + * @param string $file + */ + public function buildFile($interchange, $file) + { $parser = new HTMLPurifier_StringHashParser(); $this->build( $interchange, - new HTMLPurifier_StringHash( $parser->parseFile($file) ) + new HTMLPurifier_StringHash($parser->parseFile($file)) ); } /** * Builds an interchange object based on a hash. - * @param $interchange HTMLPurifier_ConfigSchema_Interchange object to build - * @param $hash HTMLPurifier_ConfigSchema_StringHash source data + * @param HTMLPurifier_ConfigSchema_Interchange $interchange HTMLPurifier_ConfigSchema_Interchange object to build + * @param HTMLPurifier_StringHash $hash source data + * @throws HTMLPurifier_ConfigSchema_Exception */ - public function build($interchange, $hash) { + public function build($interchange, $hash) + { if (!$hash instanceof HTMLPurifier_StringHash) { $hash = new HTMLPurifier_StringHash($hash); } @@ -75,7 +99,13 @@ public function build($interchange, $hash) { $this->_findUnused($hash); } - public function buildDirective($interchange, $hash) { + /** + * @param HTMLPurifier_ConfigSchema_Interchange $interchange + * @param HTMLPurifier_StringHash $hash + * @throws HTMLPurifier_ConfigSchema_Exception + */ + public function buildDirective($interchange, $hash) + { $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive(); // These are required elements: @@ -84,7 +114,9 @@ public function buildDirective($interchange, $hash) { if (isset($hash['TYPE'])) { $type = explode('/', $hash->offsetGet('TYPE')); - if (isset($type[1])) $directive->typeAllowsNull = true; + if (isset($type[1])) { + $directive->typeAllowsNull = true; + } $directive->type = $type[0]; } else { throw new HTMLPurifier_ConfigSchema_Exception("TYPE in directive hash '$id' not defined"); @@ -92,7 +124,11 @@ public function buildDirective($interchange, $hash) { if (isset($hash['DEFAULT'])) { try { - $directive->default = $this->varParser->parse($hash->offsetGet('DEFAULT'), $directive->type, $directive->typeAllowsNull); + $directive->default = $this->varParser->parse( + $hash->offsetGet('DEFAULT'), + $directive->type, + $directive->typeAllowsNull + ); } catch (HTMLPurifier_VarParserException $e) { throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'"); } @@ -139,34 +175,45 @@ public function buildDirective($interchange, $hash) { /** * Evaluates an array PHP code string without array() wrapper + * @param string $contents */ - protected function evalArray($contents) { - return eval('return array('. $contents .');'); + protected function evalArray($contents) + { + return eval('return array(' . $contents . ');'); } /** * Converts an array list into a lookup array. + * @param array $array + * @return array */ - protected function lookup($array) { + protected function lookup($array) + { $ret = array(); - foreach ($array as $val) $ret[$val] = true; + foreach ($array as $val) { + $ret[$val] = true; + } return $ret; } /** * Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id * object based on a string Id. + * @param string $id + * @return HTMLPurifier_ConfigSchema_Interchange_Id */ - protected function id($id) { + protected function id($id) + { return HTMLPurifier_ConfigSchema_Interchange_Id::make($id); } /** * Triggers errors for any unused keys passed in the hash; such keys * may indicate typos, missing values, etc. - * @param $hash Instance of ConfigSchema_StringHash to check. + * @param HTMLPurifier_StringHash $hash Hash to check. */ - protected function _findUnused($hash) { + protected function _findUnused($hash) + { $accessed = $hash->getAccessed(); foreach ($hash as $k => $v) { if (!isset($accessed[$k])) { @@ -174,7 +221,6 @@ protected function _findUnused($hash) { } } } - } // vim: et sw=4 sts=4 diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/Validator.php b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/Validator.php index 5772493a7f5..9f14444f375 100644 --- a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/Validator.php +++ b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/Validator.php @@ -12,36 +12,48 @@ class HTMLPurifier_ConfigSchema_Validator { /** - * Easy to access global objects. + * @type HTMLPurifier_ConfigSchema_Interchange */ - protected $interchange, $aliases; + protected $interchange; + + /** + * @type array + */ + protected $aliases; /** * Context-stack to provide easy to read error messages. + * @type array */ protected $context = array(); /** - * HTMLPurifier_VarParser to test default's type. + * to test default's type. + * @type HTMLPurifier_VarParser */ protected $parser; - public function __construct() { + public function __construct() + { $this->parser = new HTMLPurifier_VarParser(); } /** - * Validates a fully-formed interchange object. Throws an - * HTMLPurifier_ConfigSchema_Exception if there's a problem. + * Validates a fully-formed interchange object. + * @param HTMLPurifier_ConfigSchema_Interchange $interchange + * @return bool */ - public function validate($interchange) { + public function validate($interchange) + { $this->interchange = $interchange; $this->aliases = array(); // PHP is a bit lax with integer <=> string conversions in // arrays, so we don't use the identical !== comparison foreach ($interchange->directives as $i => $directive) { $id = $directive->id->toString(); - if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'"); + if ($i != $id) { + $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'"); + } $this->validateDirective($directive); } return true; @@ -49,8 +61,10 @@ public function validate($interchange) { /** * Validates a HTMLPurifier_ConfigSchema_Interchange_Id object. + * @param HTMLPurifier_ConfigSchema_Interchange_Id $id */ - public function validateId($id) { + public function validateId($id) + { $id_string = $id->toString(); $this->context[] = "id '$id_string'"; if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) { @@ -67,8 +81,10 @@ public function validateId($id) { /** * Validates a HTMLPurifier_ConfigSchema_Interchange_Directive object. + * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d */ - public function validateDirective($d) { + public function validateDirective($d) + { $id = $d->id->toString(); $this->context[] = "directive '$id'"; $this->validateId($d->id); @@ -108,9 +124,13 @@ public function validateDirective($d) { /** * Extra validation if $allowed member variable of * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. + * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d */ - public function validateDirectiveAllowed($d) { - if (is_null($d->allowed)) return; + public function validateDirectiveAllowed($d) + { + if (is_null($d->allowed)) { + return; + } $this->with($d, 'allowed') ->assertNotEmpty() ->assertIsLookup(); // handled by InterchangeBuilder @@ -119,7 +139,9 @@ public function validateDirectiveAllowed($d) { } $this->context[] = 'allowed'; foreach ($d->allowed as $val => $x) { - if (!is_string($val)) $this->error("value $val", 'must be a string'); + if (!is_string($val)) { + $this->error("value $val", 'must be a string'); + } } array_pop($this->context); } @@ -127,15 +149,23 @@ public function validateDirectiveAllowed($d) { /** * Extra validation if $valueAliases member variable of * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. + * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d */ - public function validateDirectiveValueAliases($d) { - if (is_null($d->valueAliases)) return; + public function validateDirectiveValueAliases($d) + { + if (is_null($d->valueAliases)) { + return; + } $this->with($d, 'valueAliases') ->assertIsArray(); // handled by InterchangeBuilder $this->context[] = 'valueAliases'; foreach ($d->valueAliases as $alias => $real) { - if (!is_string($alias)) $this->error("alias $alias", 'must be a string'); - if (!is_string($real)) $this->error("alias target $real from alias '$alias'", 'must be a string'); + if (!is_string($alias)) { + $this->error("alias $alias", 'must be a string'); + } + if (!is_string($real)) { + $this->error("alias target $real from alias '$alias'", 'must be a string'); + } if ($alias === $real) { $this->error("alias '$alias'", "must not be an alias to itself"); } @@ -155,8 +185,10 @@ public function validateDirectiveValueAliases($d) { /** * Extra validation if $aliases member variable of * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. + * @param HTMLPurifier_ConfigSchema_Interchange_Directive $d */ - public function validateDirectiveAliases($d) { + public function validateDirectiveAliases($d) + { $this->with($d, 'aliases') ->assertIsArray(); // handled by InterchangeBuilder $this->context[] = 'aliases'; @@ -180,27 +212,37 @@ public function validateDirectiveAliases($d) { /** * Convenience function for generating HTMLPurifier_ConfigSchema_ValidatorAtom * for validating simple member variables of objects. + * @param $obj + * @param $member + * @return HTMLPurifier_ConfigSchema_ValidatorAtom */ - protected function with($obj, $member) { + protected function with($obj, $member) + { return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member); } /** * Emits an error, providing helpful context. + * @throws HTMLPurifier_ConfigSchema_Exception */ - protected function error($target, $msg) { - if ($target !== false) $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext(); - else $prefix = ucfirst($this->getFormattedContext()); + protected function error($target, $msg) + { + if ($target !== false) { + $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext(); + } else { + $prefix = ucfirst($this->getFormattedContext()); + } throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg)); } /** * Returns a formatted context string. + * @return string */ - protected function getFormattedContext() { + protected function getFormattedContext() + { return implode(' in ', array_reverse($this->context)); } - } // vim: et sw=4 sts=4 diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/ValidatorAtom.php b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/ValidatorAtom.php index 7bb887bc414..a2e0b4a1b32 100644 --- a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/ValidatorAtom.php +++ b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/ValidatorAtom.php @@ -8,59 +8,123 @@ */ class HTMLPurifier_ConfigSchema_ValidatorAtom { + /** + * @type string + */ + protected $context; - protected $context, $obj, $member, $contents; + /** + * @type object + */ + protected $obj; - public function __construct($context, $obj, $member) { - $this->context = $context; - $this->obj = $obj; - $this->member = $member; - $this->contents =& $obj->$member; + /** + * @type string + */ + protected $member; + + /** + * @type mixed + */ + protected $contents; + + public function __construct($context, $obj, $member) + { + $this->context = $context; + $this->obj = $obj; + $this->member = $member; + $this->contents =& $obj->$member; } - public function assertIsString() { - if (!is_string($this->contents)) $this->error('must be a string'); + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertIsString() + { + if (!is_string($this->contents)) { + $this->error('must be a string'); + } return $this; } - public function assertIsBool() { - if (!is_bool($this->contents)) $this->error('must be a boolean'); + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertIsBool() + { + if (!is_bool($this->contents)) { + $this->error('must be a boolean'); + } return $this; } - public function assertIsArray() { - if (!is_array($this->contents)) $this->error('must be an array'); + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertIsArray() + { + if (!is_array($this->contents)) { + $this->error('must be an array'); + } return $this; } - public function assertNotNull() { - if ($this->contents === null) $this->error('must not be null'); + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertNotNull() + { + if ($this->contents === null) { + $this->error('must not be null'); + } return $this; } - public function assertAlnum() { + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertAlnum() + { $this->assertIsString(); - if (!ctype_alnum($this->contents)) $this->error('must be alphanumeric'); + if (!ctype_alnum($this->contents)) { + $this->error('must be alphanumeric'); + } return $this; } - public function assertNotEmpty() { - if (empty($this->contents)) $this->error('must not be empty'); + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertNotEmpty() + { + if (empty($this->contents)) { + $this->error('must not be empty'); + } return $this; } - public function assertIsLookup() { + /** + * @return HTMLPurifier_ConfigSchema_ValidatorAtom + */ + public function assertIsLookup() + { $this->assertIsArray(); foreach ($this->contents as $v) { - if ($v !== true) $this->error('must be a lookup array'); + if ($v !== true) { + $this->error('must be a lookup array'); + } } return $this; } - protected function error($msg) { + /** + * @param string $msg + * @throws HTMLPurifier_ConfigSchema_Exception + */ + protected function error($msg) + { throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($this->member) . ' in ' . $this->context . ' ' . $msg); } - } // vim: et sw=4 sts=4 diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/schema.ser b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/schema.ser index fa0bacb9476cab9e69889141969c6fefd2b4419c..22ea32185db63b19d525f509ebe431f593e92271 100644 GIT binary patch delta 163 zcmZ2bGNW{YIitm9dm&#|RU->4CFlI2R6WO>ocwZ+{Nj?l#N5=-yp+_U;$)z>l68`m n!Q_2f(wpb9F61FcS0vBmzsiP;hMV=2AH7 delta 52 xcmbPHx}ao&IivaHdKKA?p$4p**Rt;9LE#qiO!ieVWHi`ZuQG{kv!!-CKLF_+5n2EM diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt index be932b1cf77..1cc3fcda2f3 100644 --- a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt +++ b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt @@ -2,9 +2,11 @@ Core.EscapeInvalidChildren TYPE: bool DEFAULT: false --DESCRIPTION-- -When true, a child is found that is not allowed in the context of the +

          Warning: this configuration option is no longer does anything as of 4.6.0.

          + +

          When true, a child is found that is not allowed in the context of the parent element will be transformed into text as if it were ASCII. When false, that element and all internal tags will be dropped, though text will be preserved. There is no option for dropping the element but preserving -child nodes. +child nodes.

          --# vim: et sw=4 sts=4 diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt index 3b50c8c1310..123b6e26b80 100644 --- a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt +++ b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt @@ -11,7 +11,7 @@ DEFAULT: NULL to check if a URI has passed through HTML Purifier with this line:

          -
          $checksum === sha1($secret_key . ':' . $url)
          +
          $checksum === hash_hmac("sha256", $url, $secret_key)

          If the output is TRUE, the redirector script should accept the URI. diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Language/messages/en.php b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Language/messages/en.php index 0f9bd73d91f..1fa30bdfedd 100644 --- a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Language/messages/en.php +++ b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Language/messages/en.php @@ -4,60 +4,52 @@ $messages = array( -'HTMLPurifier' => 'HTML Purifier', - + 'HTMLPurifier' => 'HTML Purifier', // for unit testing purposes -'LanguageFactoryTest: Pizza' => 'Pizza', -'LanguageTest: List' => '$1', -'LanguageTest: Hash' => '$1.Keys; $1.Values', - -'Item separator' => ', ', -'Item separator last' => ' and ', // non-Harvard style - -'ErrorCollector: No errors' => 'No errors detected. However, because error reporting is still incomplete, there may have been errors that the error collector was not notified of; please inspect the output HTML carefully.', -'ErrorCollector: At line' => ' at line $line', -'ErrorCollector: Incidental errors' => 'Incidental errors', - -'Lexer: Unclosed comment' => 'Unclosed comment', -'Lexer: Unescaped lt' => 'Unescaped less-than sign (<) should be <', -'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped', -'Lexer: Missing attribute key' => 'Attribute declaration has no key', -'Lexer: Missing end quote' => 'Attribute declaration has no end quote', -'Lexer: Extracted body' => 'Removed document metadata tags', - -'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized', -'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1', -'Strategy_RemoveForeignElements: Foreign element to text' => 'Unrecognized $CurrentToken.Serialized tag converted to text', -'Strategy_RemoveForeignElements: Foreign element removed' => 'Unrecognized $CurrentToken.Serialized tag removed', -'Strategy_RemoveForeignElements: Comment removed' => 'Comment containing "$CurrentToken.Data" removed', -'Strategy_RemoveForeignElements: Foreign meta element removed' => 'Unrecognized $CurrentToken.Serialized meta tag and all descendants removed', -'Strategy_RemoveForeignElements: Token removed to end' => 'Tags and text starting from $1 element where removed to end', -'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' => 'Trailing hyphen(s) in comment removed', -'Strategy_RemoveForeignElements: Hyphens in comment collapsed' => 'Double hyphens in comments are not allowed, and were collapsed into single hyphens', - -'Strategy_MakeWellFormed: Unnecessary end tag removed' => 'Unnecessary $CurrentToken.Serialized tag removed', -'Strategy_MakeWellFormed: Unnecessary end tag to text' => 'Unnecessary $CurrentToken.Serialized tag converted to text', -'Strategy_MakeWellFormed: Tag auto closed' => '$1.Compact started on line $1.Line auto-closed by $CurrentToken.Compact', -'Strategy_MakeWellFormed: Tag carryover' => '$1.Compact started on line $1.Line auto-continued into $CurrentToken.Compact', -'Strategy_MakeWellFormed: Stray end tag removed' => 'Stray $CurrentToken.Serialized tag removed', -'Strategy_MakeWellFormed: Stray end tag to text' => 'Stray $CurrentToken.Serialized tag converted to text', -'Strategy_MakeWellFormed: Tag closed by element end' => '$1.Compact tag started on line $1.Line closed by end of $CurrentToken.Serialized', -'Strategy_MakeWellFormed: Tag closed by document end' => '$1.Compact tag started on line $1.Line closed by end of document', - -'Strategy_FixNesting: Node removed' => '$CurrentToken.Compact node removed', -'Strategy_FixNesting: Node excluded' => '$CurrentToken.Compact node removed due to descendant exclusion by ancestor element', -'Strategy_FixNesting: Node reorganized' => 'Contents of $CurrentToken.Compact node reorganized to enforce its content model', -'Strategy_FixNesting: Node contents removed' => 'Contents of $CurrentToken.Compact node removed', - -'AttrValidator: Attributes transformed' => 'Attributes on $CurrentToken.Compact transformed from $1.Keys to $2.Keys', -'AttrValidator: Attribute removed' => '$CurrentAttr.Name attribute on $CurrentToken.Compact removed', - + 'LanguageFactoryTest: Pizza' => 'Pizza', + 'LanguageTest: List' => '$1', + 'LanguageTest: Hash' => '$1.Keys; $1.Values', + 'Item separator' => ', ', + 'Item separator last' => ' and ', // non-Harvard style + + 'ErrorCollector: No errors' => 'No errors detected. However, because error reporting is still incomplete, there may have been errors that the error collector was not notified of; please inspect the output HTML carefully.', + 'ErrorCollector: At line' => ' at line $line', + 'ErrorCollector: Incidental errors' => 'Incidental errors', + 'Lexer: Unclosed comment' => 'Unclosed comment', + 'Lexer: Unescaped lt' => 'Unescaped less-than sign (<) should be <', + 'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped', + 'Lexer: Missing attribute key' => 'Attribute declaration has no key', + 'Lexer: Missing end quote' => 'Attribute declaration has no end quote', + 'Lexer: Extracted body' => 'Removed document metadata tags', + 'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized', + 'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1', + 'Strategy_RemoveForeignElements: Foreign element to text' => 'Unrecognized $CurrentToken.Serialized tag converted to text', + 'Strategy_RemoveForeignElements: Foreign element removed' => 'Unrecognized $CurrentToken.Serialized tag removed', + 'Strategy_RemoveForeignElements: Comment removed' => 'Comment containing "$CurrentToken.Data" removed', + 'Strategy_RemoveForeignElements: Foreign meta element removed' => 'Unrecognized $CurrentToken.Serialized meta tag and all descendants removed', + 'Strategy_RemoveForeignElements: Token removed to end' => 'Tags and text starting from $1 element where removed to end', + 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' => 'Trailing hyphen(s) in comment removed', + 'Strategy_RemoveForeignElements: Hyphens in comment collapsed' => 'Double hyphens in comments are not allowed, and were collapsed into single hyphens', + 'Strategy_MakeWellFormed: Unnecessary end tag removed' => 'Unnecessary $CurrentToken.Serialized tag removed', + 'Strategy_MakeWellFormed: Unnecessary end tag to text' => 'Unnecessary $CurrentToken.Serialized tag converted to text', + 'Strategy_MakeWellFormed: Tag auto closed' => '$1.Compact started on line $1.Line auto-closed by $CurrentToken.Compact', + 'Strategy_MakeWellFormed: Tag carryover' => '$1.Compact started on line $1.Line auto-continued into $CurrentToken.Compact', + 'Strategy_MakeWellFormed: Stray end tag removed' => 'Stray $CurrentToken.Serialized tag removed', + 'Strategy_MakeWellFormed: Stray end tag to text' => 'Stray $CurrentToken.Serialized tag converted to text', + 'Strategy_MakeWellFormed: Tag closed by element end' => '$1.Compact tag started on line $1.Line closed by end of $CurrentToken.Serialized', + 'Strategy_MakeWellFormed: Tag closed by document end' => '$1.Compact tag started on line $1.Line closed by end of document', + 'Strategy_FixNesting: Node removed' => '$CurrentToken.Compact node removed', + 'Strategy_FixNesting: Node excluded' => '$CurrentToken.Compact node removed due to descendant exclusion by ancestor element', + 'Strategy_FixNesting: Node reorganized' => 'Contents of $CurrentToken.Compact node reorganized to enforce its content model', + 'Strategy_FixNesting: Node contents removed' => 'Contents of $CurrentToken.Compact node removed', + 'AttrValidator: Attributes transformed' => 'Attributes on $CurrentToken.Compact transformed from $1.Keys to $2.Keys', + 'AttrValidator: Attribute removed' => '$CurrentAttr.Name attribute on $CurrentToken.Compact removed', ); $errorNames = array( - E_ERROR => 'Error', + E_ERROR => 'Error', E_WARNING => 'Warning', - E_NOTICE => 'Notice' + E_NOTICE => 'Notice' ); // vim: et sw=4 sts=4 diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer.php b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer.php index 84e86116f00..16acd415740 100644 --- a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer.php +++ b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer.php @@ -7,25 +7,30 @@ class HTMLPurifier_Printer { /** - * Instance of HTMLPurifier_Generator for HTML generation convenience funcs + * For HTML generation convenience funcs. + * @type HTMLPurifier_Generator */ protected $generator; /** - * Instance of HTMLPurifier_Config, for easy access + * For easy access. + * @type HTMLPurifier_Config */ protected $config; /** * Initialize $generator. */ - public function __construct() { + public function __construct() + { } /** * Give generator necessary configuration if possible + * @param HTMLPurifier_Config $config */ - public function prepareGenerator($config) { + public function prepareGenerator($config) + { $all = $config->getAll(); $context = new HTMLPurifier_Context(); $this->generator = new HTMLPurifier_Generator($config, $context); @@ -39,45 +44,62 @@ public function prepareGenerator($config) { /** * Returns a start tag - * @param $tag Tag name - * @param $attr Attribute array + * @param string $tag Tag name + * @param array $attr Attribute array + * @return string */ - protected function start($tag, $attr = array()) { + protected function start($tag, $attr = array()) + { return $this->generator->generateFromToken( - new HTMLPurifier_Token_Start($tag, $attr ? $attr : array()) - ); + new HTMLPurifier_Token_Start($tag, $attr ? $attr : array()) + ); } /** - * Returns an end teg - * @param $tag Tag name + * Returns an end tag + * @param string $tag Tag name + * @return string */ - protected function end($tag) { + protected function end($tag) + { return $this->generator->generateFromToken( - new HTMLPurifier_Token_End($tag) - ); + new HTMLPurifier_Token_End($tag) + ); } /** * Prints a complete element with content inside - * @param $tag Tag name - * @param $contents Element contents - * @param $attr Tag attributes - * @param $escape Bool whether or not to escape contents + * @param string $tag Tag name + * @param string $contents Element contents + * @param array $attr Tag attributes + * @param bool $escape whether or not to escape contents + * @return string */ - protected function element($tag, $contents, $attr = array(), $escape = true) { + protected function element($tag, $contents, $attr = array(), $escape = true) + { return $this->start($tag, $attr) . - ($escape ? $this->escape($contents) : $contents) . - $this->end($tag); + ($escape ? $this->escape($contents) : $contents) . + $this->end($tag); } - protected function elementEmpty($tag, $attr = array()) { + /** + * @param string $tag + * @param array $attr + * @return string + */ + protected function elementEmpty($tag, $attr = array()) + { return $this->generator->generateFromToken( new HTMLPurifier_Token_Empty($tag, $attr) ); } - protected function text($text) { + /** + * @param string $text + * @return string + */ + protected function text($text) + { return $this->generator->generateFromToken( new HTMLPurifier_Token_Text($text) ); @@ -85,24 +107,29 @@ protected function text($text) { /** * Prints a simple key/value row in a table. - * @param $name Key - * @param $value Value + * @param string $name Key + * @param mixed $value Value + * @return string */ - protected function row($name, $value) { - if (is_bool($value)) $value = $value ? 'On' : 'Off'; + protected function row($name, $value) + { + if (is_bool($value)) { + $value = $value ? 'On' : 'Off'; + } return $this->start('tr') . "\n" . - $this->element('th', $name) . "\n" . - $this->element('td', $value) . "\n" . - $this->end('tr') - ; + $this->element('th', $name) . "\n" . + $this->element('td', $value) . "\n" . + $this->end('tr'); } /** * Escapes a string for HTML output. - * @param $string String to escape + * @param string $string String to escape + * @return string */ - protected function escape($string) { + protected function escape($string) + { $string = HTMLPurifier_Encoder::cleanUTF8($string); $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8'); return $string; @@ -110,32 +137,46 @@ protected function escape($string) { /** * Takes a list of strings and turns them into a single list - * @param $array List of strings - * @param $polite Bool whether or not to add an end before the last + * @param string[] $array List of strings + * @param bool $polite Bool whether or not to add an end before the last + * @return string */ - protected function listify($array, $polite = false) { - if (empty($array)) return 'None'; + protected function listify($array, $polite = false) + { + if (empty($array)) { + return 'None'; + } $ret = ''; $i = count($array); foreach ($array as $value) { $i--; $ret .= $value; - if ($i > 0 && !($polite && $i == 1)) $ret .= ', '; - if ($polite && $i == 1) $ret .= 'and '; + if ($i > 0 && !($polite && $i == 1)) { + $ret .= ', '; + } + if ($polite && $i == 1) { + $ret .= 'and '; + } } return $ret; } /** * Retrieves the class of an object without prefixes, as well as metadata - * @param $obj Object to determine class of - * @param $prefix Further prefix to remove + * @param object $obj Object to determine class of + * @param string $sec_prefix Further prefix to remove + * @return string */ - protected function getClass($obj, $sec_prefix = '') { + protected function getClass($obj, $sec_prefix = '') + { static $five = null; - if ($five === null) $five = version_compare(PHP_VERSION, '5', '>='); + if ($five === null) { + $five = version_compare(PHP_VERSION, '5', '>='); + } $prefix = 'HTMLPurifier_' . $sec_prefix; - if (!$five) $prefix = strtolower($prefix); + if (!$five) { + $prefix = strtolower($prefix); + } $class = str_replace($prefix, '', get_class($obj)); $lclass = strtolower($class); $class .= '('; @@ -164,13 +205,14 @@ protected function getClass($obj, $sec_prefix = '') { break; case 'css_importantdecorator': $class .= $this->getClass($obj->def, $sec_prefix); - if ($obj->allow) $class .= ', !important'; + if ($obj->allow) { + $class .= ', !important'; + } break; } $class .= ')'; return $class; } - } // vim: et sw=4 sts=4 diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/CSSDefinition.php b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/CSSDefinition.php index 0714580cf3e..afc8c18ab96 100644 --- a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/CSSDefinition.php +++ b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/CSSDefinition.php @@ -2,10 +2,17 @@ class HTMLPurifier_Printer_CSSDefinition extends HTMLPurifier_Printer { - + /** + * @type HTMLPurifier_CSSDefinition + */ protected $def; - public function render($config) { + /** + * @param HTMLPurifier_Config $config + * @return string + */ + public function render($config) + { $this->def = $config->getCSSDefinition(); $ret = ''; @@ -32,7 +39,6 @@ public function render($config) { return $ret; } - } // vim: et sw=4 sts=4 diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/ConfigForm.php b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/ConfigForm.php index b41476f941c..660960f3795 100644 --- a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/ConfigForm.php +++ b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/ConfigForm.php @@ -7,17 +7,20 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer { /** - * Printers for specific fields + * Printers for specific fields. + * @type HTMLPurifier_Printer[] */ protected $fields = array(); /** - * Documentation URL, can have fragment tagged on end + * Documentation URL, can have fragment tagged on end. + * @type string */ protected $docURL; /** - * Name of form element to stuff config in + * Name of form element to stuff config in. + * @type string */ protected $name; @@ -25,24 +28,27 @@ class HTMLPurifier_Printer_ConfigForm extends HTMLPurifier_Printer * Whether or not to compress directive names, clipping them off * after a certain amount of letters. False to disable or integer letters * before clipping. + * @type bool */ protected $compress = false; /** - * @param $name Form element name for directives to be stuffed into - * @param $doc_url String documentation URL, will have fragment tagged on - * @param $compress Integer max length before compressing a directive name, set to false to turn off + * @param string $name Form element name for directives to be stuffed into + * @param string $doc_url String documentation URL, will have fragment tagged on + * @param bool $compress Integer max length before compressing a directive name, set to false to turn off */ public function __construct( - $name, $doc_url = null, $compress = false + $name, + $doc_url = null, + $compress = false ) { parent::__construct(); $this->docURL = $doc_url; - $this->name = $name; + $this->name = $name; $this->compress = $compress; // initialize sub-printers - $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default(); - $this->fields[HTMLPurifier_VarParser::BOOL] = new HTMLPurifier_Printer_ConfigForm_bool(); + $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default(); + $this->fields[HTMLPurifier_VarParser::BOOL] = new HTMLPurifier_Printer_ConfigForm_bool(); } /** @@ -50,32 +56,42 @@ public function __construct( * @param $cols Integer columns of textarea, null to use default * @param $rows Integer rows of textarea, null to use default */ - public function setTextareaDimensions($cols = null, $rows = null) { - if ($cols) $this->fields['default']->cols = $cols; - if ($rows) $this->fields['default']->rows = $rows; + public function setTextareaDimensions($cols = null, $rows = null) + { + if ($cols) { + $this->fields['default']->cols = $cols; + } + if ($rows) { + $this->fields['default']->rows = $rows; + } } /** * Retrieves styling, in case it is not accessible by webserver */ - public static function getCSS() { + public static function getCSS() + { return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css'); } /** * Retrieves JavaScript, in case it is not accessible by webserver */ - public static function getJavaScript() { + public static function getJavaScript() + { return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js'); } /** * Returns HTML output for a configuration form - * @param $config Configuration object of current form state, or an array + * @param HTMLPurifier_Config|array $config Configuration object of current form state, or an array * where [0] has an HTML namespace and [1] is being rendered. - * @param $allowed Optional namespace(s) and directives to restrict form to. + * @param array|bool $allowed Optional namespace(s) and directives to restrict form to. + * @param bool $render_controls + * @return string */ - public function render($config, $allowed = true, $render_controls = true) { + public function render($config, $allowed = true, $render_controls = true) + { if (is_array($config) && isset($config[0])) { $gen_config = $config[0]; $config = $config[1]; @@ -91,29 +107,29 @@ public function render($config, $allowed = true, $render_controls = true) { $all = array(); foreach ($allowed as $key) { list($ns, $directive) = $key; - $all[$ns][$directive] = $config->get($ns .'.'. $directive); + $all[$ns][$directive] = $config->get($ns . '.' . $directive); } $ret = ''; $ret .= $this->start('table', array('class' => 'hp-config')); $ret .= $this->start('thead'); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive')); - $ret .= $this->element('th', 'Value', array('class' => 'hp-value')); + $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive')); + $ret .= $this->element('th', 'Value', array('class' => 'hp-value')); $ret .= $this->end('tr'); $ret .= $this->end('thead'); foreach ($all as $ns => $directives) { $ret .= $this->renderNamespace($ns, $directives); } if ($render_controls) { - $ret .= $this->start('tbody'); - $ret .= $this->start('tr'); - $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls')); - $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit')); - $ret .= '[Reset]'; - $ret .= $this->end('td'); - $ret .= $this->end('tr'); - $ret .= $this->end('tbody'); + $ret .= $this->start('tbody'); + $ret .= $this->start('tr'); + $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls')); + $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit')); + $ret .= '[Reset]'; + $ret .= $this->end('td'); + $ret .= $this->end('tr'); + $ret .= $this->end('tbody'); } $ret .= $this->end('table'); return $ret; @@ -122,13 +138,15 @@ public function render($config, $allowed = true, $render_controls = true) { /** * Renders a single namespace * @param $ns String namespace name - * @param $directive Associative array of directives to values + * @param array $directives array of directives to values + * @return string */ - protected function renderNamespace($ns, $directives) { + protected function renderNamespace($ns, $directives) + { $ret = ''; $ret .= $this->start('tbody', array('class' => 'namespace')); $ret .= $this->start('tr'); - $ret .= $this->element('th', $ns, array('colspan' => 2)); + $ret .= $this->element('th', $ns, array('colspan' => 2)); $ret .= $this->end('tr'); $ret .= $this->end('tbody'); $ret .= $this->start('tbody'); @@ -139,40 +157,44 @@ protected function renderNamespace($ns, $directives) { $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL); $ret .= $this->start('a', array('href' => $url)); } - $attr = array('for' => "{$this->name}:$ns.$directive"); - - // crop directive name if it's too long - if (!$this->compress || (strlen($directive) < $this->compress)) { - $directive_disp = $directive; - } else { - $directive_disp = substr($directive, 0, $this->compress - 2) . '...'; - $attr['title'] = $directive; - } + $attr = array('for' => "{$this->name}:$ns.$directive"); + + // crop directive name if it's too long + if (!$this->compress || (strlen($directive) < $this->compress)) { + $directive_disp = $directive; + } else { + $directive_disp = substr($directive, 0, $this->compress - 2) . '...'; + $attr['title'] = $directive; + } - $ret .= $this->element( - 'label', - $directive_disp, - // component printers must create an element with this id - $attr - ); - if ($this->docURL) $ret .= $this->end('a'); + $ret .= $this->element( + 'label', + $directive_disp, + // component printers must create an element with this id + $attr + ); + if ($this->docURL) { + $ret .= $this->end('a'); + } $ret .= $this->end('th'); $ret .= $this->start('td'); - $def = $this->config->def->info["$ns.$directive"]; - if (is_int($def)) { - $allow_null = $def < 0; - $type = abs($def); - } else { - $type = $def->type; - $allow_null = isset($def->allow_null); - } - if (!isset($this->fields[$type])) $type = 0; // default - $type_obj = $this->fields[$type]; - if ($allow_null) { - $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj); - } - $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config)); + $def = $this->config->def->info["$ns.$directive"]; + if (is_int($def)) { + $allow_null = $def < 0; + $type = abs($def); + } else { + $type = $def->type; + $allow_null = isset($def->allow_null); + } + if (!isset($this->fields[$type])) { + $type = 0; + } // default + $type_obj = $this->fields[$type]; + if ($allow_null) { + $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj); + } + $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config)); $ret .= $this->end('td'); $ret .= $this->end('tr'); } @@ -185,19 +207,33 @@ protected function renderNamespace($ns, $directives) { /** * Printer decorator for directives that accept null */ -class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer { +class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer +{ /** * Printer being decorated + * @type HTMLPurifier_Printer */ protected $obj; + /** - * @param $obj Printer to decorate + * @param HTMLPurifier_Printer $obj Printer to decorate */ - public function __construct($obj) { + public function __construct($obj) + { parent::__construct(); $this->obj = $obj; } - public function render($ns, $directive, $value, $name, $config) { + + /** + * @param string $ns + * @param string $directive + * @param string $value + * @param string $name + * @param HTMLPurifier_Config|array $config + * @return string + */ + public function render($ns, $directive, $value, $name, $config) + { if (is_array($config) && isset($config[0])) { $gen_config = $config[0]; $config = $config[1]; @@ -215,15 +251,19 @@ public function render($ns, $directive, $value, $name, $config) { 'type' => 'checkbox', 'value' => '1', 'class' => 'null-toggle', - 'name' => "$name"."[Null_$ns.$directive]", + 'name' => "$name" . "[Null_$ns.$directive]", 'id' => "$name:Null_$ns.$directive", 'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!! ); if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) { // modify inline javascript slightly - $attr['onclick'] = "toggleWriteability('$name:Yes_$ns.$directive',checked);toggleWriteability('$name:No_$ns.$directive',checked)"; + $attr['onclick'] = + "toggleWriteability('$name:Yes_$ns.$directive',checked);" . + "toggleWriteability('$name:No_$ns.$directive',checked)"; + } + if ($value === null) { + $attr['checked'] = 'checked'; } - if ($value === null) $attr['checked'] = 'checked'; $ret .= $this->elementEmpty('input', $attr); $ret .= $this->text(' or '); $ret .= $this->elementEmpty('br'); @@ -235,10 +275,28 @@ public function render($ns, $directive, $value, $name, $config) { /** * Swiss-army knife configuration form field printer */ -class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer { +class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer +{ + /** + * @type int + */ public $cols = 18; + + /** + * @type int + */ public $rows = 5; - public function render($ns, $directive, $value, $name, $config) { + + /** + * @param string $ns + * @param string $directive + * @param string $value + * @param string $name + * @param HTMLPurifier_Config|array $config + * @return string + */ + public function render($ns, $directive, $value, $name, $config) + { if (is_array($config) && isset($config[0])) { $gen_config = $config[0]; $config = $config[1]; @@ -262,6 +320,7 @@ public function render($ns, $directive, $value, $name, $config) { foreach ($array as $val => $b) { $value[] = $val; } + //TODO does this need a break? case HTMLPurifier_VarParser::ALIST: $value = implode(PHP_EOL, $value); break; @@ -281,25 +340,27 @@ public function render($ns, $directive, $value, $name, $config) { $value = serialize($value); } $attr = array( - 'name' => "$name"."[$ns.$directive]", + 'name' => "$name" . "[$ns.$directive]", 'id' => "$name:$ns.$directive" ); - if ($value === null) $attr['disabled'] = 'disabled'; + if ($value === null) { + $attr['disabled'] = 'disabled'; + } if (isset($def->allowed)) { $ret .= $this->start('select', $attr); foreach ($def->allowed as $val => $b) { $attr = array(); - if ($value == $val) $attr['selected'] = 'selected'; + if ($value == $val) { + $attr['selected'] = 'selected'; + } $ret .= $this->element('option', $val, $attr); } $ret .= $this->end('select'); - } elseif ( - $type === HTMLPurifier_VarParser::TEXT || - $type === HTMLPurifier_VarParser::ITEXT || - $type === HTMLPurifier_VarParser::ALIST || - $type === HTMLPurifier_VarParser::HASH || - $type === HTMLPurifier_VarParser::LOOKUP - ) { + } elseif ($type === HTMLPurifier_VarParser::TEXT || + $type === HTMLPurifier_VarParser::ITEXT || + $type === HTMLPurifier_VarParser::ALIST || + $type === HTMLPurifier_VarParser::HASH || + $type === HTMLPurifier_VarParser::LOOKUP) { $attr['cols'] = $this->cols; $attr['rows'] = $this->rows; $ret .= $this->start('textarea', $attr); @@ -317,8 +378,18 @@ public function render($ns, $directive, $value, $name, $config) { /** * Bool form field printer */ -class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer { - public function render($ns, $directive, $value, $name, $config) { +class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer +{ + /** + * @param string $ns + * @param string $directive + * @param string $value + * @param string $name + * @param HTMLPurifier_Config|array $config + * @return string + */ + public function render($ns, $directive, $value, $name, $config) + { if (is_array($config) && isset($config[0])) { $gen_config = $config[0]; $config = $config[1]; @@ -336,12 +407,16 @@ public function render($ns, $directive, $value, $name, $config) { $attr = array( 'type' => 'radio', - 'name' => "$name"."[$ns.$directive]", + 'name' => "$name" . "[$ns.$directive]", 'id' => "$name:Yes_$ns.$directive", 'value' => '1' ); - if ($value === true) $attr['checked'] = 'checked'; - if ($value === null) $attr['disabled'] = 'disabled'; + if ($value === true) { + $attr['checked'] = 'checked'; + } + if ($value === null) { + $attr['disabled'] = 'disabled'; + } $ret .= $this->elementEmpty('input', $attr); $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive")); @@ -351,12 +426,16 @@ public function render($ns, $directive, $value, $name, $config) { $attr = array( 'type' => 'radio', - 'name' => "$name"."[$ns.$directive]", + 'name' => "$name" . "[$ns.$directive]", 'id' => "$name:No_$ns.$directive", 'value' => '0' ); - if ($value === false) $attr['checked'] = 'checked'; - if ($value === null) $attr['disabled'] = 'disabled'; + if ($value === false) { + $attr['checked'] = 'checked'; + } + if ($value === null) { + $attr['disabled'] = 'disabled'; + } $ret .= $this->elementEmpty('input', $attr); $ret .= $this->end('div'); diff --git a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/HTMLDefinition.php b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/HTMLDefinition.php index e17ddd51ff3..679d19ba3a1 100644 --- a/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/HTMLDefinition.php +++ b/framework/vendors/htmlpurifier/standalone/HTMLPurifier/Printer/HTMLDefinition.php @@ -4,11 +4,16 @@ class HTMLPurifier_Printer_HTMLDefinition extends HTMLPurifier_Printer { /** - * Instance of HTMLPurifier_HTMLDefinition, for easy access + * @type HTMLPurifier_HTMLDefinition, for easy access */ protected $def; - public function render($config) { + /** + * @param HTMLPurifier_Config $config + * @return string + */ + public function render($config) + { $ret = ''; $this->config =& $config; @@ -28,8 +33,10 @@ public function render($config) { /** * Renders the Doctype table + * @return string */ - protected function renderDoctype() { + protected function renderDoctype() + { $doctype = $this->def->doctype; $ret = ''; $ret .= $this->start('table'); @@ -45,8 +52,10 @@ protected function renderDoctype() { /** * Renders environment table, which is miscellaneous info + * @return string */ - protected function renderEnvironment() { + protected function renderEnvironment() + { $def = $this->def; $ret = ''; @@ -59,28 +68,28 @@ protected function renderEnvironment() { $ret .= $this->row('Block wrap name', $def->info_block_wrapper); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Global attributes'); - $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr),0,0); + $ret .= $this->element('th', 'Global attributes'); + $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr), null, 0); $ret .= $this->end('tr'); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Tag transforms'); - $list = array(); - foreach ($def->info_tag_transform as $old => $new) { - $new = $this->getClass($new, 'TagTransform_'); - $list[] = "<$old> with $new"; - } - $ret .= $this->element('td', $this->listify($list)); + $ret .= $this->element('th', 'Tag transforms'); + $list = array(); + foreach ($def->info_tag_transform as $old => $new) { + $new = $this->getClass($new, 'TagTransform_'); + $list[] = "<$old> with $new"; + } + $ret .= $this->element('td', $this->listify($list)); $ret .= $this->end('tr'); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Pre-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre)); + $ret .= $this->element('th', 'Pre-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre)); $ret .= $this->end('tr'); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Post-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post)); + $ret .= $this->element('th', 'Post-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post)); $ret .= $this->end('tr'); $ret .= $this->end('table'); @@ -89,8 +98,10 @@ protected function renderEnvironment() { /** * Renders the Content Sets table + * @return string */ - protected function renderContentSets() { + protected function renderContentSets() + { $ret = ''; $ret .= $this->start('table'); $ret .= $this->element('caption', 'Content Sets'); @@ -106,8 +117,10 @@ protected function renderContentSets() { /** * Renders the Elements ($info) table + * @return string */ - protected function renderInfo() { + protected function renderInfo() + { $ret = ''; $ret .= $this->start('table'); $ret .= $this->element('caption', 'Elements ($info)'); @@ -118,39 +131,39 @@ protected function renderInfo() { $ret .= $this->end('tr'); foreach ($this->def->info as $name => $def) { $ret .= $this->start('tr'); - $ret .= $this->element('th', "<$name>", array('class'=>'heavy', 'colspan' => 2)); + $ret .= $this->element('th', "<$name>", array('class' => 'heavy', 'colspan' => 2)); $ret .= $this->end('tr'); $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Inline content'); - $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No'); + $ret .= $this->element('th', 'Inline content'); + $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No'); $ret .= $this->end('tr'); if (!empty($def->excludes)) { $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Excludes'); - $ret .= $this->element('td', $this->listifyTagLookup($def->excludes)); + $ret .= $this->element('th', 'Excludes'); + $ret .= $this->element('td', $this->listifyTagLookup($def->excludes)); $ret .= $this->end('tr'); } if (!empty($def->attr_transform_pre)) { $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Pre-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre)); + $ret .= $this->element('th', 'Pre-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre)); $ret .= $this->end('tr'); } if (!empty($def->attr_transform_post)) { $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Post-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post)); + $ret .= $this->element('th', 'Post-AttrTransform'); + $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post)); $ret .= $this->end('tr'); } if (!empty($def->auto_close)) { $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Auto closed by'); - $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close)); + $ret .= $this->element('th', 'Auto closed by'); + $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close)); $ret .= $this->end('tr'); } $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Allowed attributes'); - $ret .= $this->element('td',$this->listifyAttr($def->attr), array(), 0); + $ret .= $this->element('th', 'Allowed attributes'); + $ret .= $this->element('td', $this->listifyAttr($def->attr), array(), 0); $ret .= $this->end('tr'); if (!empty($def->required_attr)) { @@ -165,64 +178,94 @@ protected function renderInfo() { /** * Renders a row describing the allowed children of an element - * @param $def HTMLPurifier_ChildDef of pertinent element + * @param HTMLPurifier_ChildDef $def HTMLPurifier_ChildDef of pertinent element + * @return string */ - protected function renderChildren($def) { + protected function renderChildren($def) + { $context = new HTMLPurifier_Context(); $ret = ''; $ret .= $this->start('tr'); - $elements = array(); - $attr = array(); - if (isset($def->elements)) { - if ($def->type == 'strictblockquote') { - $def->validateChildren(array(), $this->config, $context); - } - $elements = $def->elements; + $elements = array(); + $attr = array(); + if (isset($def->elements)) { + if ($def->type == 'strictblockquote') { + $def->validateChildren(array(), $this->config, $context); } - if ($def->type == 'chameleon') { - $attr['rowspan'] = 2; - } elseif ($def->type == 'empty') { - $elements = array(); - } elseif ($def->type == 'table') { - $elements = array_flip(array('col', 'caption', 'colgroup', 'thead', - 'tfoot', 'tbody', 'tr')); - } - $ret .= $this->element('th', 'Allowed children', $attr); - - if ($def->type == 'chameleon') { - - $ret .= $this->element('td', - 'Block: ' . - $this->escape($this->listifyTagLookup($def->block->elements)),0,0); - $ret .= $this->end('tr'); - $ret .= $this->start('tr'); - $ret .= $this->element('td', - 'Inline: ' . - $this->escape($this->listifyTagLookup($def->inline->elements)),0,0); - - } elseif ($def->type == 'custom') { + $elements = $def->elements; + } + if ($def->type == 'chameleon') { + $attr['rowspan'] = 2; + } elseif ($def->type == 'empty') { + $elements = array(); + } elseif ($def->type == 'table') { + $elements = array_flip( + array( + 'col', + 'caption', + 'colgroup', + 'thead', + 'tfoot', + 'tbody', + 'tr' + ) + ); + } + $ret .= $this->element('th', 'Allowed children', $attr); - $ret .= $this->element('td', ''.ucfirst($def->type).': ' . - $def->dtd_regex); + if ($def->type == 'chameleon') { - } else { - $ret .= $this->element('td', - ''.ucfirst($def->type).': ' . - $this->escape($this->listifyTagLookup($elements)),0,0); - } + $ret .= $this->element( + 'td', + 'Block: ' . + $this->escape($this->listifyTagLookup($def->block->elements)), + null, + 0 + ); + $ret .= $this->end('tr'); + $ret .= $this->start('tr'); + $ret .= $this->element( + 'td', + 'Inline: ' . + $this->escape($this->listifyTagLookup($def->inline->elements)), + null, + 0 + ); + + } elseif ($def->type == 'custom') { + + $ret .= $this->element( + 'td', + '' . ucfirst($def->type) . ': ' . + $def->dtd_regex + ); + + } else { + $ret .= $this->element( + 'td', + '' . ucfirst($def->type) . ': ' . + $this->escape($this->listifyTagLookup($elements)), + null, + 0 + ); + } $ret .= $this->end('tr'); return $ret; } /** * Listifies a tag lookup table. - * @param $array Tag lookup array in form of array('tagname' => true) + * @param array $array Tag lookup array in form of array('tagname' => true) + * @return string */ - protected function listifyTagLookup($array) { + protected function listifyTagLookup($array) + { ksort($array); $list = array(); foreach ($array as $name => $discard) { - if ($name !== '#PCDATA' && !isset($this->def->info[$name])) continue; + if ($name !== '#PCDATA' && !isset($this->def->info[$name])) { + continue; + } $list[] = $name; } return $this->listify($list); @@ -230,13 +273,15 @@ protected function listifyTagLookup($array) { /** * Listifies a list of objects by retrieving class names and internal state - * @param $array List of objects + * @param array $array List of objects + * @return string * @todo Also add information about internal state */ - protected function listifyObjectList($array) { + protected function listifyObjectList($array) + { ksort($array); $list = array(); - foreach ($array as $discard => $obj) { + foreach ($array as $obj) { $list[] = $this->getClass($obj, 'AttrTransform_'); } return $this->listify($list); @@ -244,13 +289,17 @@ protected function listifyObjectList($array) { /** * Listifies a hash of attributes to AttrDef classes - * @param $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef) + * @param array $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef) + * @return string */ - protected function listifyAttr($array) { + protected function listifyAttr($array) + { ksort($array); $list = array(); foreach ($array as $name => $obj) { - if ($obj === false) continue; + if ($obj === false) { + continue; + } $list[] = "$name = " . $this->getClass($obj, 'AttrDef_') . ''; } return $this->listify($list); @@ -258,15 +307,18 @@ protected function listifyAttr($array) { /** * Creates a heavy header row + * @param string $text + * @param int $num + * @return string */ - protected function heavyHeader($text, $num = 1) { + protected function heavyHeader($text, $num = 1) + { $ret = ''; $ret .= $this->start('tr'); $ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy')); $ret .= $this->end('tr'); return $ret; } - } // vim: et sw=4 sts=4 diff --git a/framework/vendors/jquery/LICENSE.txt b/framework/vendors/jquery/LICENSE.txt index 0815862c5e2..5ca4047d2b3 100644 --- a/framework/vendors/jquery/LICENSE.txt +++ b/framework/vendors/jquery/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2012 jQuery Foundation and other contributors +Copyright 2014 jQuery Foundation and other contributors http://jquery.com/ Permission is hereby granted, free of charge, to any person obtaining diff --git a/framework/vendors/jqueryui/MIT-LICENSE.txt b/framework/vendors/jqueryui/MIT-LICENSE.txt index 7aaf1e8dbe7..c36fa561b6a 100644 --- a/framework/vendors/jqueryui/MIT-LICENSE.txt +++ b/framework/vendors/jqueryui/MIT-LICENSE.txt @@ -1,10 +1,14 @@ -Copyright 2012 jQuery Foundation and other contributors, -http://jqueryui.com/ +Copyright 2007, 2014 jQuery Foundation and other contributors, +https://jquery.org/ This software consists of voluntary contributions made by many -individuals (AUTHORS.txt, http://jqueryui.com/about) For exact -contribution history, see the revision history and logs, available -at http://jquery-ui.googlecode.com/svn/ +individuals. For exact contribution history, see the revision history +available at https://github.com/jquery/jquery-ui + +The following license applies to all parts of this software except as +documented below: + +==== Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -23,4 +27,18 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code contained within the demos directory. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +All files located in the node_modules and external directories are +externally maintained libraries used by this software which have their +own licenses; we recommend you read them, as their terms may differ from +the terms above. \ No newline at end of file diff --git a/framework/views/ca/error.php b/framework/views/ca/error.php old mode 100644 new mode 100755 diff --git a/framework/views/ca/error400.php b/framework/views/ca/error400.php old mode 100644 new mode 100755 diff --git a/framework/views/ca/error403.php b/framework/views/ca/error403.php old mode 100644 new mode 100755 diff --git a/framework/views/ca/error404.php b/framework/views/ca/error404.php old mode 100644 new mode 100755 diff --git a/framework/views/ca/error500.php b/framework/views/ca/error500.php old mode 100644 new mode 100755 diff --git a/framework/views/ca/error503.php b/framework/views/ca/error503.php old mode 100644 new mode 100755 diff --git a/framework/views/ca/log-firebug.php b/framework/views/ca/log-firebug.php old mode 100644 new mode 100755 diff --git a/framework/views/ca/log.php b/framework/views/ca/log.php old mode 100644 new mode 100755 diff --git a/framework/views/ca/profile-callstack-firebug.php b/framework/views/ca/profile-callstack-firebug.php old mode 100644 new mode 100755 diff --git a/framework/views/ca/profile-callstack.php b/framework/views/ca/profile-callstack.php old mode 100644 new mode 100755 diff --git a/framework/views/ca/profile-summary-firebug.php b/framework/views/ca/profile-summary-firebug.php old mode 100644 new mode 100755 diff --git a/framework/views/ca/profile-summary.php b/framework/views/ca/profile-summary.php old mode 100644 new mode 100755 diff --git a/framework/views/error403.php b/framework/views/error403.php index c6ecf61b5e6..e58900960eb 100644 --- a/framework/views/error403.php +++ b/framework/views/error403.php @@ -4,7 +4,7 @@ -Unauthorized +Forbidden -

          Unauthorized

          +

          Forbidden

          You do not have the proper credential to access this page. diff --git a/framework/views/ja/error403.php b/framework/views/ja/error403.php index f6220dcaef8..aae953ca27d 100644 --- a/framework/views/ja/error403.php +++ b/framework/views/ja/error403.php @@ -4,7 +4,7 @@ -403 未認証 +403 禁止 -

          403 未認証

          +

          403 禁止

          あなたはこのページをアクセスする正当な許可を得ていません。 diff --git a/framework/web/CAssetManager.php b/framework/web/CAssetManager.php index 38708d455a7..d7e7963eeda 100644 --- a/framework/web/CAssetManager.php +++ b/framework/web/CAssetManager.php @@ -211,7 +211,7 @@ public function publish($path,$hashByName=false,$level=-1,$forceCopy=null) throw new CException(Yii::t('yii','The "forceCopy" and "linkAssets" cannot be both true.')); if(isset($this->_published[$path])) return $this->_published[$path]; - elseif(($src=realpath($path))!==false) + elseif(is_string($path) && ($src=realpath($path))!==false) { $dir=$this->generatePath($src,$hashByName); $dstDir=$this->getBasePath().DIRECTORY_SEPARATOR.$dir; @@ -271,7 +271,7 @@ public function publish($path,$hashByName=false,$level=-1,$forceCopy=null) */ public function getPublishedPath($path,$hashByName=false) { - if(($path=realpath($path))!==false) + if(is_string($path) && ($path=realpath($path))!==false) { $base=$this->getBasePath().DIRECTORY_SEPARATOR.$this->generatePath($path,$hashByName); return is_file($path) ? $base.DIRECTORY_SEPARATOR.basename($path) : $base ; @@ -295,7 +295,7 @@ public function getPublishedUrl($path,$hashByName=false) { if(isset($this->_published[$path])) return $this->_published[$path]; - if(($path=realpath($path))!==false) + if(is_string($path) && ($path=realpath($path))!==false) { $base=$this->getBaseUrl().'/'.$this->generatePath($path,$hashByName); return is_file($path) ? $base.'/'.basename($path) : $base; @@ -325,9 +325,9 @@ protected function hash($path) protected function generatePath($file,$hashByName=false) { if (is_file($file)) - $pathForHashing=$hashByName ? basename($file) : dirname($file).filemtime($file); + $pathForHashing=$hashByName ? dirname($file) : dirname($file).filemtime($file); else - $pathForHashing=$hashByName ? basename($file) : $file.filemtime($file); + $pathForHashing=$hashByName ? $file : $file.filemtime($file); return $this->hash($pathForHashing); } diff --git a/framework/web/CBaseController.php b/framework/web/CBaseController.php index 9f5d649f7ac..e738f17656a 100644 --- a/framework/web/CBaseController.php +++ b/framework/web/CBaseController.php @@ -163,8 +163,16 @@ public function widget($className,$properties=array(),$captureOutput=false) { ob_start(); ob_implicit_flush(false); - $widget=$this->createWidget($className,$properties); - $widget->run(); + try + { + $widget=$this->createWidget($className,$properties); + $widget->run(); + } + catch(Exception $e) + { + ob_end_clean(); + throw $e; + } return ob_get_clean(); } else diff --git a/framework/web/CClientScript.php b/framework/web/CClientScript.php index f7d3c20ccc5..586fb47fc02 100644 --- a/framework/web/CClientScript.php +++ b/framework/web/CClientScript.php @@ -318,13 +318,14 @@ protected function renderScriptBatch(array $scripts) $scriptContent = $scriptValue['content']; unset($scriptValue['content']); $scriptHtmlOptions = $scriptValue; + ksort($scriptHtmlOptions); } else { $scriptContent = $scriptValue; $scriptHtmlOptions = array(); } - $key=serialize(ksort($scriptHtmlOptions)); + $key=serialize($scriptHtmlOptions); $scriptBatches[$key]['htmlOptions']=$scriptHtmlOptions; $scriptBatches[$key]['scripts'][]=$scriptContent; } @@ -553,7 +554,7 @@ public function getPackageBaseUrl($name) * Registers a script package that is listed in {@link packages}. * This method is the same as {@link registerCoreScript}. * @param string $name the name of the script package. - * @return CClientScript the CClientScript object itself (to support method chaining, available since version 1.1.5). + * @return static the CClientScript object itself (to support method chaining, available since version 1.1.5). * @since 1.1.7 * @see renderCoreScript */ @@ -565,7 +566,7 @@ public function registerPackage($name) /** * Registers a script package that is listed in {@link packages}. * @param string $name the name of the script package. - * @return CClientScript the CClientScript object itself (to support method chaining, available since version 1.1.5). + * @return static the CClientScript object itself (to support method chaining, available since version 1.1.5). * @see renderCoreScript */ public function registerCoreScript($name) @@ -593,6 +594,11 @@ public function registerCoreScript($name) $params=func_get_args(); $this->recordCachingAction('clientScript','registerCoreScript',$params); } + elseif(YII_DEBUG) + throw new CException('There is no CClientScript package: '.$name); + else + Yii::log('There is no CClientScript package: '.$name,CLogger::LEVEL_WARNING,'system.web.CClientScript'); + return $this; } @@ -600,7 +606,7 @@ public function registerCoreScript($name) * Registers a CSS file * @param string $url URL of the CSS file * @param string $media media that the CSS file should be applied to. If empty, it means all media types. - * @return CClientScript the CClientScript object itself (to support method chaining, available since version 1.1.5). + * @return static the CClientScript object itself (to support method chaining, available since version 1.1.5). */ public function registerCssFile($url,$media='') { @@ -616,7 +622,7 @@ public function registerCssFile($url,$media='') * @param string $id ID that uniquely identifies this piece of CSS code * @param string $css the CSS code * @param string $media media that the CSS code should be applied to. If empty, it means all media types. - * @return CClientScript the CClientScript object itself (to support method chaining, available since version 1.1.5). + * @return static the CClientScript object itself (to support method chaining, available since version 1.1.5). */ public function registerCss($id,$css,$media='') { @@ -637,7 +643,7 @@ public function registerCss($id,$css,$media='') *

        • CClientScript::POS_END : the script is inserted at the end of the body section.
        • *
        * @param array $htmlOptions additional HTML attributes - * @return CClientScript the CClientScript object itself (to support method chaining, available since version 1.1.5). + * @return static the CClientScript object itself (to support method chaining, available since version 1.1.5). */ public function registerScriptFile($url,$position=null,array $htmlOptions=array()) { @@ -671,7 +677,7 @@ public function registerScriptFile($url,$position=null,array $htmlOptions=array( *
      * @param array $htmlOptions additional HTML attributes * Note: HTML attributes are not allowed for script positions "CClientScript::POS_LOAD" and "CClientScript::POS_READY". - * @return CClientScript the CClientScript object itself (to support method chaining, available since version 1.1.5). + * @return static the CClientScript object itself (to support method chaining, available since version 1.1.5). */ public function registerScript($id,$script,$position=null,array $htmlOptions=array()) { @@ -711,7 +717,7 @@ public function registerScript($id,$script,$position=null,array $htmlOptions=arr * @param string $httpEquiv http-equiv attribute of the meta tag. If null, the attribute will not be generated * @param array $options other options in name-value pairs (e.g. 'scheme', 'lang') * @param string $id Optional id of the meta tag to avoid duplicates - * @return CClientScript the CClientScript object itself (to support method chaining, available since version 1.1.5). + * @return static the CClientScript object itself (to support method chaining, available since version 1.1.5). */ public function registerMetaTag($content,$name=null,$httpEquiv=null,$options=array(),$id=null) { @@ -734,7 +740,7 @@ public function registerMetaTag($content,$name=null,$httpEquiv=null,$options=arr * @param string $href href attribute of the link tag. If null, the attribute will not be generated. * @param string $media media attribute of the link tag. If null, the attribute will not be generated. * @param array $options other options in name-value pairs - * @return CClientScript the CClientScript object itself (to support method chaining, available since version 1.1.5). + * @return static the CClientScript object itself (to support method chaining, available since version 1.1.5). */ public function registerLinkTag($relation=null,$type=null,$href=null,$media=null,$options=array()) { @@ -829,7 +835,7 @@ protected function recordCachingAction($context,$method,$params) * @param string $name the name of the script package. * @param array $definition the definition array of the script package, * @see CClientScript::packages. - * @return CClientScript the CClientScript object itself (to support method chaining, available since version 1.1.10). + * @return static the CClientScript object itself (to support method chaining, available since version 1.1.10). * * @since 1.1.9 */ diff --git a/framework/web/CDbHttpSession.php b/framework/web/CDbHttpSession.php index 9b1e58b9695..a5ecf918649 100644 --- a/framework/web/CDbHttpSession.php +++ b/framework/web/CDbHttpSession.php @@ -246,9 +246,8 @@ public function writeSession($id,$data) { $expire=time()+$this->getTimeout(); $db=$this->getDbConnection(); - - if($db->getDriverName()=='pgsql' ) - $data=new CDbExpression("convert_to(".$db->quoteValue($data).", 'UTF8')"); + if($db->getDriverName()=='pgsql' ) + $data=new CDbExpression("convert_to(".$db->quoteValue($data).", 'UTF8')"); if($db->getDriverName()=='sqlsrv' || $db->getDriverName()=='mssql' || $db->getDriverName()=='dblib') $data=new CDbExpression('CONVERT(VARBINARY(MAX), '.$db->quoteValue($data).')'); if($db->createCommand()->select('id')->from($this->sessionTableName)->where('id=:id',array(':id'=>$id))->queryScalar()===false) diff --git a/framework/web/CHttpRequest.php b/framework/web/CHttpRequest.php index f83efad8c57..2075d8b70e3 100644 --- a/framework/web/CHttpRequest.php +++ b/framework/web/CHttpRequest.php @@ -32,10 +32,11 @@ * @property string $requestUri The request URI portion for the currently requested URL. * @property string $queryString Part of the request URL that is after the question mark. * @property boolean $isSecureConnection If the request is sent via secure channel (https). - * @property string $requestType Request type, such as GET, POST, HEAD, PUT, DELETE. + * @property string $requestType Request type, such as GET, POST, HEAD, PUT, PATCH, DELETE. * @property boolean $isPostRequest Whether this is a POST request. * @property boolean $isDeleteRequest Whether this is a DELETE request. * @property boolean $isPutRequest Whether this is a PUT request. + * @property boolean $isPatchRequest Whether this is a PATCH request. * @property boolean $isAjaxRequest Whether this is an AJAX (XMLHttpRequest) request. * @property boolean $isFlashRequest Whether this is an Adobe Flash or Adobe Flex request. * @property string $serverName Server name. @@ -99,6 +100,7 @@ class CHttpRequest extends CApplicationComponent private $_preferredLanguages; private $_csrfToken; private $_restParams; + private $_httpVersion; /** * Initializes the application component. @@ -250,7 +252,32 @@ public function getPut($name,$defaultValue=null) } /** - * Returns request parameters. Typically PUT or DELETE. + * Returns the named PATCH parameter value. + * If the PATCH parameter does not exist or if the current request is not a PATCH request, + * the second parameter to this method will be returned. + * If the PATCH request was tunneled through POST via _method parameter, the POST parameter + * will be returned instead. + * @param string $name the PATCH parameter name + * @param mixed $defaultValue the default parameter value if the PATCH parameter does not exist. + * @return mixed the PATCH parameter value + * @since 1.1.16 + */ + public function getPatch($name,$defaultValue=null) + { + if($this->getIsPatchViaPostRequest()) + return $this->getPost($name, $defaultValue); + + if($this->getIsPatchRequest()) + { + $restParams=$this->getRestParams(); + return isset($restParams[$name]) ? $restParams[$name] : $defaultValue; + } + else + return $defaultValue; + } + + /** + * Returns request parameters. Typically PUT, PATCH or DELETE. * @return array the request parameters * @since 1.1.7 * @since 1.1.13 method became public @@ -446,7 +473,15 @@ public function getPathInfo() else throw new CException(Yii::t('yii','CHttpRequest is unable to determine the path info of the request.')); - $this->_pathInfo=trim($pathInfo,'/'); + if($pathInfo==='/') + $pathInfo=''; + elseif($pathInfo[0]==='/') + $pathInfo=substr($pathInfo,1); + + if(($posEnd=strlen($pathInfo)-1)>0 && $pathInfo[$posEnd]==='/') + $pathInfo=substr($pathInfo,0,$posEnd); + + $this->_pathInfo=$pathInfo; } return $this->_pathInfo; } @@ -537,21 +572,23 @@ public function getQueryString() */ public function getIsSecureConnection() { - return isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']=='on' || $_SERVER['HTTPS']==1) - || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO']=='https'; + return isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'],'on')===0 || $_SERVER['HTTPS']==1) + || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'],'https')===0; } /** - * Returns the request type, such as GET, POST, HEAD, PUT, DELETE. + * Returns the request type, such as GET, POST, HEAD, PUT, PATCH, DELETE. * Request type can be manually set in POST requests with a parameter named _method. Useful - * for RESTful request from older browsers which do not support PUT or DELETE + * for RESTful request from older browsers which do not support PUT, PATCH or DELETE * natively (available since version 1.1.11). - * @return string request type, such as GET, POST, HEAD, PUT, DELETE. + * @return string request type, such as GET, POST, HEAD, PUT, PATCH, DELETE. */ public function getRequestType() { if(isset($_POST['_method'])) return strtoupper($_POST['_method']); + elseif(isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) + return strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); return strtoupper(isset($_SERVER['REQUEST_METHOD'])?$_SERVER['REQUEST_METHOD']:'GET'); } @@ -605,6 +642,26 @@ protected function getIsPutViaPostRequest() return isset($_POST['_method']) && !strcasecmp($_POST['_method'],'PUT'); } + /** + * Returns whether this is a PATCH request. + * @return boolean whether this is a PATCH request. + * @since 1.1.16 + */ + public function getIsPatchRequest() + { + return (isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'],'PATCH')) || $this->getIsPatchViaPostRequest(); + } + + /** + * Returns whether this is a PATCH request which was tunneled through POST. + * @return boolean whether this is a PATCH request tunneled through POST. + * @since 1.1.16 + */ + protected function getIsPatchViaPostRequest() + { + return isset($_POST['_method']) && !strcasecmp($_POST['_method'],'PATCH'); + } + /** * Returns whether this is an AJAX (XMLHttpRequest) request. * @return boolean whether this is an AJAX (XMLHttpRequest) request. @@ -975,14 +1032,31 @@ public function getPreferredLanguages() } /** - * Returns the user preferred language. - * The returned language ID will be canonicalized using {@link CLocale::getCanonicalID}. - * @return string the user preferred language or false if the user does not have any. + * Returns the user-preferred language that should be used by this application. + * The language resolution is based on the user preferred languages and the languages + * supported by the application. The method will try to find the best match. + * @param array $languages a list of the languages supported by the application. + * If empty, this method will return the first language returned by [[getPreferredLanguages()]]. + * @return string the language that the application should use. false is returned if both [[getPreferredLanguages()]] + * and `$languages` are empty. */ - public function getPreferredLanguage() + public function getPreferredLanguage($languages=array()) { $preferredLanguages=$this->getPreferredLanguages(); - return !empty($preferredLanguages) ? CLocale::getCanonicalID($preferredLanguages[0]) : false; + if(empty($languages)) { + return !empty($preferredLanguages) ? CLocale::getCanonicalID($preferredLanguages[0]) : false; + } + foreach ($preferredLanguages as $preferredLanguage) { + $preferredLanguage=CLocale::getCanonicalID($preferredLanguage); + foreach ($languages as $language) { + $language=CLocale::getCanonicalID($language); + // en_us==en_us, en==en_us, en_us==en + if($language===$acceptedLanguage || strpos($acceptedLanguage,$language.'_')===0 || strpos($language,$acceptedLanguage.'_')===0) { + return $language; + } + } + } + return reset($languages); } /** @@ -1004,6 +1078,7 @@ public function sendFile($fileName,$content,$mimeType=null,$terminate=true) $contentStart=0; $contentEnd=$fileSize-1; + $httpVersion=$this->getHttpVersion(); if(isset($_SERVER['HTTP_RANGE'])) { header('Accept-Ranges: bytes'); @@ -1045,11 +1120,11 @@ public function sendFile($fileName,$content,$mimeType=null,$terminate=true) throw new CHttpException(416,'Requested Range Not Satisfiable'); } - header('HTTP/1.1 206 Partial Content'); + header("HTTP/$httpVersion 206 Partial Content"); header("Content-Range: bytes $contentStart-$contentEnd/$fileSize"); } else - header('HTTP/1.1 200 OK'); + header("HTTP/$httpVersion 200 OK"); $length=$contentEnd-$contentStart+1; // Calculate new content length @@ -1219,6 +1294,7 @@ public function validateCsrfToken($event) { if ($this->getIsPostRequest() || $this->getIsPutRequest() || + $this->getIsPatchRequest() || $this->getIsDeleteRequest()) { $cookies=$this->getCookies(); @@ -1232,6 +1308,9 @@ public function validateCsrfToken($event) case 'PUT': $userToken=$this->getPut($this->csrfTokenName); break; + case 'PATCH': + $userToken=$this->getPatch($this->csrfTokenName); + break; case 'DELETE': $userToken=$this->getDelete($this->csrfTokenName); } @@ -1247,6 +1326,25 @@ public function validateCsrfToken($event) throw new CHttpException(400,Yii::t('yii','The CSRF token could not be verified.')); } } + + + /** + * Returns the version of the HTTP protocol used by client. + * + * @return string the version of the HTTP protocol. + * @since 1.1.16 + */ + public function getHttpVersion() + { + if($this->_httpVersion===null) + { + if(isset($_SERVER['SERVER_PROTOCOL']) && $_SERVER['SERVER_PROTOCOL']==='HTTP/1.0') + $this->_httpVersion='1.0'; + else + $this->_httpVersion='1.1'; + } + return $this->_httpVersion; + } } diff --git a/framework/web/CHttpSession.php b/framework/web/CHttpSession.php index 0554b692aa0..e01266eb6a5 100644 --- a/framework/web/CHttpSession.php +++ b/framework/web/CHttpSession.php @@ -178,7 +178,8 @@ public function setSessionID($value) */ public function regenerateID($deleteOldSession=false) { - session_regenerate_id($deleteOldSession); + if($this->getIsStarted()) + session_regenerate_id($deleteOldSession); } /** diff --git a/framework/web/CSort.php b/framework/web/CSort.php index 9fb5fdb103a..5353a71d50c 100644 --- a/framework/web/CSort.php +++ b/framework/web/CSort.php @@ -245,9 +245,9 @@ public function getOrderBy($criteria=null) if(is_array($definition)) { if($descending) - $orders[]=isset($definition['desc']) ? $definition['desc'] : $attribute.' DESC'; + $orders[]=isset($definition['desc']) ? (is_array($definition['desc']) ? implode(', ',$definition['desc']) : $definition['desc']) : $attribute.' DESC'; else - $orders[]=isset($definition['asc']) ? $definition['asc'] : $attribute; + $orders[]=isset($definition['asc']) ? (is_array($definition['asc']) ? implode(', ',$definition['asc']) : $definition['asc']) : $attribute; } elseif($definition!==false) { @@ -469,4 +469,4 @@ protected function createLink($attribute,$label,$url,$htmlOptions) { return CHtml::link($label,$url,$htmlOptions); } -} \ No newline at end of file +} diff --git a/framework/web/CUploadedFile.php b/framework/web/CUploadedFile.php index 1548b36b9f7..8e9ccf05d75 100644 --- a/framework/web/CUploadedFile.php +++ b/framework/web/CUploadedFile.php @@ -267,9 +267,6 @@ public function getHasError() */ public function getExtensionName() { - if(($pos=strrpos($this->_name,'.'))!==false) - return (string)substr($this->_name,$pos+1); - else - return ''; + return CFileHelper::getExtension($this->_name); } } diff --git a/framework/web/CUrlManager.php b/framework/web/CUrlManager.php index cca699f41b1..688a3ab0dd9 100644 --- a/framework/web/CUrlManager.php +++ b/framework/web/CUrlManager.php @@ -658,6 +658,7 @@ public function __construct($route,$pattern) $this->route=trim($route,'/'); $tr2['/']=$tr['/']='\\/'; + $tr['.']='\\.'; if(strpos($route,'<')!==false && preg_match_all('/<(\w+)>/',$route,$matches2)) { diff --git a/framework/web/CWebApplication.php b/framework/web/CWebApplication.php index fa651d43470..8d63a12b70f 100644 --- a/framework/web/CWebApplication.php +++ b/framework/web/CWebApplication.php @@ -346,7 +346,7 @@ public function createController($route,$owner=null) $classFile=$basePath.DIRECTORY_SEPARATOR.$className.'.php'; if($owner->controllerNamespace!==null) - $className=$owner->controllerNamespace.'\\'.$className; + $className=$owner->controllerNamespace.'\\'.str_replace('/','\\',$controllerID).$className; if(is_file($classFile)) { diff --git a/framework/web/actions/CAction.php b/framework/web/actions/CAction.php index ab04e5ead1d..b2dad0efda1 100644 --- a/framework/web/actions/CAction.php +++ b/framework/web/actions/CAction.php @@ -72,8 +72,9 @@ public function runWithParams($params) $method=new ReflectionMethod($this, 'run'); if($method->getNumberOfParameters()>0) return $this->runWithParamsInternal($this, $method, $params); - else - return $this->run(); + + $this->run(); + return true; } /** diff --git a/framework/web/actions/CInlineAction.php b/framework/web/actions/CInlineAction.php index da6581f15bd..d93225fc01e 100644 --- a/framework/web/actions/CInlineAction.php +++ b/framework/web/actions/CInlineAction.php @@ -45,8 +45,8 @@ public function runWithParams($params) $method=new ReflectionMethod($controller, $methodName); if($method->getNumberOfParameters()>0) return $this->runWithParamsInternal($controller, $method, $params); - else - return $controller->$methodName(); - } + $controller->$methodName(); + return true; + } } diff --git a/framework/web/actions/CViewAction.php b/framework/web/actions/CViewAction.php index 22886f8e998..daa3c8609e4 100644 --- a/framework/web/actions/CViewAction.php +++ b/framework/web/actions/CViewAction.php @@ -93,7 +93,7 @@ public function getRequestedView() * Resolves the user-specified view into a valid view name. * @param string $viewPath user-specified view in the format of 'path.to.view'. * @return string fully resolved view in the format of 'path/to/view'. - * @throw CHttpException if the user-specified view is invalid + * @throws CHttpException if the user-specified view is invalid */ protected function resolveView($viewPath) { diff --git a/framework/web/auth/CAuthManager.php b/framework/web/auth/CAuthManager.php index 7916f476040..7c703757c05 100644 --- a/framework/web/auth/CAuthManager.php +++ b/framework/web/auth/CAuthManager.php @@ -18,7 +18,7 @@ * Access Control (RBAC). * * The main idea is that permissions are organized as a hierarchy of - * {@link CAuthItem authorization items}. Items on higer level inherit the permissions + * {@link CAuthItem authorization items}. Items on higher level inherit the permissions * represented by items on lower level. And roles are simply top-level authorization items * that may be assigned to individual users. A user is said to have a permission * to do something if the corresponding authorization item is inherited by one of his roles. diff --git a/framework/web/auth/CDbAuthManager.php b/framework/web/auth/CDbAuthManager.php index 41d24fa3663..0a254c41d34 100644 --- a/framework/web/auth/CDbAuthManager.php +++ b/framework/web/auth/CDbAuthManager.php @@ -421,7 +421,7 @@ public function getAuthItems($type=null,$userId=null) * Creates an authorization item. * An authorization item represents an action permission (e.g. creating a post). * It has three types: operation, task and role. - * Authorization items form a hierarchy. Higher level items inheirt permissions representing + * Authorization items form a hierarchy. Higher level items inherit permissions representing * by lower level items. * @param string $name the item name. This must be a unique identifier. * @param integer $type the item type (0: operation, 1: task, 2: role). diff --git a/framework/web/auth/CPhpAuthManager.php b/framework/web/auth/CPhpAuthManager.php index bb8953ffde6..3584f1dd7f5 100644 --- a/framework/web/auth/CPhpAuthManager.php +++ b/framework/web/auth/CPhpAuthManager.php @@ -100,7 +100,7 @@ public function checkAccess($itemName,$userId,$params=array()) public function addItemChild($itemName,$childName) { if(!isset($this->_items[$childName],$this->_items[$itemName])) - throw new CException(Yii::t('yii','Either "{parent}" or "{child}" does not exist.',array('{child}'=>$childName,'{name}'=>$itemName))); + throw new CException(Yii::t('yii','Either "{parent}" or "{child}" does not exist.',array('{child}'=>$childName,'{parent}'=>$itemName))); $child=$this->_items[$childName]; $item=$this->_items[$itemName]; $this->checkItemChildType($item->getType(),$child->getType()); @@ -272,7 +272,7 @@ public function getAuthItems($type=null,$userId=null) * Creates an authorization item. * An authorization item represents an action permission (e.g. creating a post). * It has three types: operation, task and role. - * Authorization items form a hierarchy. Higher level items inheirt permissions representing + * Authorization items form a hierarchy. Higher level items inherit permissions representing * by lower level items. * @param string $name the item name. This must be a unique identifier. * @param integer $type the item type (0: operation, 1: task, 2: role). diff --git a/framework/web/filters/CHttpCacheFilter.php b/framework/web/filters/CHttpCacheFilter.php index f69d8be7543..2f1f5cf094c 100644 --- a/framework/web/filters/CHttpCacheFilter.php +++ b/framework/web/filters/CHttpCacheFilter.php @@ -180,7 +180,8 @@ protected function checkLastModified($lastModified) */ protected function send304Header() { - header('HTTP/1.1 304 Not Modified'); + $httpVersion=Yii::app()->request->getHttpVersion(); + header("HTTP/$httpVersion 304 Not Modified"); } /** diff --git a/framework/web/form/CForm.php b/framework/web/form/CForm.php index 3fc2645850d..29372a938d9 100644 --- a/framework/web/form/CForm.php +++ b/framework/web/form/CForm.php @@ -133,6 +133,14 @@ class CForm extends CFormElement implements ArrayAccess * @since 1.1.14 */ public $showErrors; + /** + * @var string|null HTML code to prepend to the list of errors in the error summary. See {@link CActiveForm::errorSummary()}. + */ + public $errorSummaryHeader; + /** + * @var string|null HTML code to append to the list of errors in the error summary. See {@link CActiveForm::errorSummary()}. + */ + public $errorSummaryFooter; /** * @var array the configuration used to create the active form widget. * The widget will be used to render the form tag and the error messages. @@ -476,7 +484,7 @@ public function renderBody() $output.="
      \n".$this->description."
      \n"; if($this->showErrorSummary && ($model=$this->getModel(false))!==null) - $output.=$this->getActiveFormWidget()->errorSummary($model)."\n"; + $output.=$this->getActiveFormWidget()->errorSummary($model,$this->errorSummaryHeader,$this->errorSummaryFooter)."\n"; $output.=$this->renderElements()."\n".$this->renderButtons()."\n"; diff --git a/framework/web/form/CFormElement.php b/framework/web/form/CFormElement.php index a8a24280df8..841a2c101e5 100644 --- a/framework/web/form/CFormElement.php +++ b/framework/web/form/CFormElement.php @@ -86,6 +86,27 @@ public function __get($name) array('{class}'=>get_class($this), '{property}'=>$name))); } + /** + * Checks a property value or an attribute value on existence or not null + * Do not call this method. This is a PHP magic method that we override + * to allow using the following syntax to read a property or attribute: + *
      +	 * isset($element->propertyName);
      +	 * 
      + * @param string $name the property or attribute name + * @return boolean + */ + public function __isset($name) + { + $getter='get'.$name; + if(method_exists($this,$getter)) + return $this->$getter()!==null; + elseif(isset($this->attributes[$name])) + return isset($this->attributes[$name]); + else + return false; + } + /** * Sets value of a property or attribute. * Do not call this method. This is a PHP magic method that we override diff --git a/framework/web/form/CFormInputElement.php b/framework/web/form/CFormInputElement.php index 0549dcbbc1c..3eb73909181 100644 --- a/framework/web/form/CFormInputElement.php +++ b/framework/web/form/CFormInputElement.php @@ -29,6 +29,13 @@ *
    • number: an HTML5 number input generated using {@link CHtml::activeNumberField}
    • *
    • range: an HTML5 range input generated using {@link CHtml::activeRangeField}
    • *
    • date: an HTML5 date input generated using {@link CHtml::activeDateField}
    • + *
    • time: an HTML5 time input generated using {@link CHtml::activeTimeField}
    • + *
    • datetime: an HTML5 datetime input generated using {@link CHtml::activeDateTimeField}
    • + *
    • datetimelocal: an HTML5 datetime-local input generated using {@link CHtml::activeDateTimeLocalField}
    • + *
    • week: an HTML5 week input generated using {@link CHtml::activeWeekField}
    • + *
    • color: an HTML5 color input generated using {@link CHtml::activeColorField}
    • + *
    • tel: an HTML5 tel input generated using {@link CHtml::activeTelField}
    • + *
    • search: an HTML5 search input generated using {@link CHtml::activeSearchField}
    • *
    * The {@link type} property can also be a class name or a path alias to the class. In this case, * the input is generated using a widget of the specified class. Note, the widget must @@ -68,7 +75,14 @@ class CFormInputElement extends CFormElement 'email'=>'activeEmailField', 'number'=>'activeNumberField', 'range'=>'activeRangeField', - 'date'=>'activeDateField' + 'date'=>'activeDateField', + 'time'=>'activeTimeField', + 'datetime'=>'activeDateTimeField', + 'datetimelocal'=>'activeDateTimeLocalField', + 'week'=>'activeWeekField', + 'color'=>'activeColorField', + 'tel'=>'activeTelField', + 'search'=>'activeSearchField', ); /** @@ -197,9 +211,7 @@ public function renderLabel() ); if(!empty($this->attributes['id'])) - { - $options['for'] = $this->attributes['id']; - } + $options['for']=$this->attributes['id']; return CHtml::activeLabel($this->getParent()->getModel(), $this->name, $options); } diff --git a/framework/web/helpers/CHtml.php b/framework/web/helpers/CHtml.php index 3758c5c6370..c8e55853c4e 100644 --- a/framework/web/helpers/CHtml.php +++ b/framework/web/helpers/CHtml.php @@ -12,6 +12,11 @@ /** * CHtml is a static class that provides a collection of helper methods for creating HTML views. * + * Nearly all of the methods in this class allow setting additional html attributes for the html + * tags they generate. You can specify for example. 'class', 'style' or 'id' for an html element. + * For example when using array('class' => 'my-class', 'target' => '_blank') as htmlOptions + * it will result in the html attributes rendered like this: class="my-class" target="_blank". + * * @author Qiang Xue * @package system.web.helpers * @since 1.0 @@ -335,6 +340,14 @@ public static function form($action='',$method='post',$htmlOptions=array()) public static function beginForm($action='',$method='post',$htmlOptions=array()) { $htmlOptions['action']=$url=self::normalizeUrl($action); + if(strcasecmp($method,'get')!==0 && strcasecmp($method,'post')!==0) + { + $customMethod=$method; + $method='post'; + } + else + $customMethod=false; + $htmlOptions['method']=$method; $form=self::tag('form',$htmlOptions,false,false); $hiddens=array(); @@ -351,8 +364,10 @@ public static function beginForm($action='',$method='post',$htmlOptions=array()) $request=Yii::app()->request; if($request->enableCsrfValidation && !strcasecmp($method,'post')) $hiddens[]=self::hiddenField($request->csrfTokenName,$request->getCsrfToken(),array('id'=>false)); + if($customMethod!==false) + $hiddens[]=self::hiddenField('_method',$customMethod); if($hiddens!==array()) - $form.="\n".self::tag('div',array('style'=>'display:none'),implode("\n",$hiddens)); + $form.="\n".implode("\n",$hiddens); return $form; } @@ -580,6 +595,23 @@ public static function label($label,$for,$htmlOptions=array()) return self::tag('label',$htmlOptions,$label); } + /** + * Generates a color picker field input. + * @param string $name the input name + * @param string $value the input value + * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see inputField + * @since 1.1.16 + */ + public static function colorField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('color',$name,$value,$htmlOptions); + } + /** * Generates a text field input. * @param string $name the input name @@ -596,6 +628,22 @@ public static function textField($name,$value='',$htmlOptions=array()) return self::inputField('text',$name,$value,$htmlOptions); } + /** + * Generates a search field input. + * @param string $name the input name + * @param string $value the input value + * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see inputField + * @since 1.1.16 + */ + public static function searchField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('search',$name,$value,$htmlOptions); + } /** * Generates a number field input. * @param string $name the input name @@ -664,6 +712,57 @@ public static function timeField($name,$value='',$htmlOptions=array()) return self::inputField('time',$name,$value,$htmlOptions); } + /** + * Generates a datetime field input. + * @param string $name the input name + * @param string $value the input value + * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see inputField + * @since 1.1.16 + */ + public static function dateTimeField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('datetime',$name,$value,$htmlOptions); + } + + /** + * Generates a local datetime field input. + * @param string $name the input name + * @param string $value the input value + * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see inputField + * @since 1.1.16 + */ + public static function dateTimeLocalField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('datetime-local',$name,$value,$htmlOptions); + } + + /** + * Generates a week field input. + * @param string $name the input name + * @param string $value the input value + * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see inputField + * @since 1.1.16 + */ + public static function weekField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('week',$name,$value,$htmlOptions); + } + /** * Generates an email field input. * @param string $name the input name @@ -1024,7 +1123,7 @@ public static function listBox($name,$select,$data,$htmlOptions=array()) public static function checkBoxList($name,$select,$data,$htmlOptions=array()) { $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}'; - $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:"
    \n"; + $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:self::tag('br'); $container=isset($htmlOptions['container'])?$htmlOptions['container']:'span'; unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']); @@ -1140,7 +1239,7 @@ public static function checkBoxList($name,$select,$data,$htmlOptions=array()) public static function radioButtonList($name,$select,$data,$htmlOptions=array()) { $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}'; - $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:"
    \n"; + $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:self::tag('br'); $container=isset($htmlOptions['container'])?$htmlOptions['container']:'span'; unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']); @@ -1151,7 +1250,7 @@ public static function radioButtonList($name,$select,$data,$htmlOptions=array()) { if(!is_array($htmlOptions['empty'])) $htmlOptions['empty']=array(''=>$htmlOptions['empty']); - $data=array_merge($htmlOptions['empty'],$data); + $data=CMap::mergeArray($htmlOptions['empty'],$data); unset($htmlOptions['empty']); } @@ -1574,6 +1673,86 @@ public static function activeTimeField($model,$attribute,$htmlOptions=array()) return self::activeInputField('time',$model,$attribute,$htmlOptions); } + /** + * Generates a datetime field input for a model attribute. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel $model the data model + * @param string $attribute the attribute + * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see activeInputField + * @since 1.1.16 + */ + public static function activeDateTimeField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + return self::activeInputField('datetime',$model,$attribute,$htmlOptions); + } + + /** + * Generates a datetime-local field input for a model attribute. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel $model the data model + * @param string $attribute the attribute + * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see activeInputField + * @since 1.1.16 + */ + public static function activeDateTimeLocalField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + return self::activeInputField('datetime-local',$model,$attribute,$htmlOptions); + } + + /** + * Generates a week field input for a model attribute. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel $model the data model + * @param string $attribute the attribute + * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see activeInputField + * @since 1.1.16 + */ + public static function activeWeekField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + return self::activeInputField('week',$model,$attribute,$htmlOptions); + } + + /** + * Generates a color picker field input for a model attribute. + * If the attribute has input error, the input field's CSS class will + * be appended with {@link errorCss}. + * @param CModel $model the data model + * @param string $attribute the attribute + * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special + * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.) + * @return string the generated input field + * @see clientChange + * @see activeInputField + * @since 1.1.16 + */ + public static function activeColorField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + return self::activeInputField('color',$model,$attribute,$htmlOptions); + } + /** * Generates a telephone field input for a model attribute. * If the attribute has input error, the input field's CSS class will @@ -1891,6 +2070,12 @@ public static function activeListBox($model,$attribute,$data,$htmlOptions=array( * or is false, the 'check all' checkbox will be displayed at the beginning of * the checkbox list. *
  • encode: boolean, specifies whether to encode HTML-encode tag attributes and values. Defaults to true.
  • + *
  • labelOptions: array, specifies the additional HTML attributes to be rendered + * for every label tag in the list.
  • + *
  • container: string, specifies the checkboxes enclosing tag. Defaults to 'span'. + * If the value is an empty string, no enclosing tag will be generated
  • + *
  • baseID: string, specifies the base ID prefix to be used for checkboxes in the list. + * This option is available since version 1.1.13.
  • * * Since 1.1.7, a special option named 'uncheckValue' is available. It can be used to set the value * that will be returned when the checkbox is not checked. By default, this value is ''. @@ -1936,9 +2121,21 @@ public static function activeCheckBoxList($model,$attribute,$data,$htmlOptions=a *
      *
    • template: string, specifies how each radio button is rendered. Defaults * to "{input} {label}", where "{input}" will be replaced by the generated - * radio button input tag while "{label}" will be replaced by the corresponding radio button label.
    • + * radio button input tag while "{label}" will be replaced by the corresponding radio button label, + * {beginLabel} will be replaced by <label> with labelOptions, {labelTitle} will be replaced + * by the corresponding radio button label title and {endLabel} will be replaced by </label> *
    • separator: string, specifies the string that separates the generated radio buttons. Defaults to new line (
      ).
    • *
    • encode: boolean, specifies whether to encode HTML-encode tag attributes and values. Defaults to true.
    • + *
    • labelOptions: array, specifies the additional HTML attributes to be rendered + * for every label tag in the list.
    • + *
    • container: string, specifies the radio buttons enclosing tag. Defaults to 'span'. + * If the value is an empty string, no enclosing tag will be generated
    • + *
    • baseID: string, specifies the base ID prefix to be used for radio buttons in the list. + * This option is available since version 1.1.13.
    • + *
    • empty: string, specifies the text corresponding to empty selection. Its value is empty. + * The 'empty' option can also be an array of value-label pairs. + * Each pair will be used to render a radio button at the beginning. Note, the text label will NOT be HTML-encoded. + * This option is available since version 1.1.14.
    • *
    * Since version 1.1.7, a special option named 'uncheckValue' is available that can be used to specify the value * returned when the radio button is not checked. By default, this value is ''. Internally, a hidden field is @@ -2200,7 +2397,7 @@ public static function setModelNameConverter($converter) else throw new CException(Yii::t('yii','The $converter argument must be a valid callback or null.')); } - + /** * Generates input field name for a model attribute. * Unlike {@link resolveName}, this method does NOT modify the attribute name. @@ -2229,7 +2426,9 @@ public static function activeName($model,$attribute) protected static function activeInputField($type,$model,$attribute,$htmlOptions) { $htmlOptions['type']=$type; - if($type==='text' || $type==='password') + if($type==='text'||$type==='password'||$type==='color'||$type==='date'||$type==='datetime'|| + $type==='datetime-local'||$type==='email'||$type==='month'||$type==='number'||$type==='range'|| + $type==='search'||$type==='tel'||$type==='time'||$type==='url'||$type==='week') { if(!isset($htmlOptions['maxlength'])) { @@ -2473,7 +2672,7 @@ public static function resolveNameID($model,&$attribute,&$htmlOptions) public static function resolveName($model,&$attribute) { $modelName=self::modelName($model); - + if(($pos=strpos($attribute,'['))!==false) { if($pos!==0) // e.g. name[a][b] @@ -2555,9 +2754,9 @@ protected static function addErrorCss(&$htmlOptions) public static function renderAttributes($htmlOptions) { static $specialAttributes=array( - 'async'=>1, 'autofocus'=>1, 'autoplay'=>1, + 'async'=>1, 'checked'=>1, 'controls'=>1, 'declare'=>1, @@ -2567,6 +2766,7 @@ public static function renderAttributes($htmlOptions) 'formnovalidate'=>1, 'hidden'=>1, 'ismap'=>1, + 'itemscope'=>1, 'loop'=>1, 'multiple'=>1, 'muted'=>1, @@ -2599,7 +2799,10 @@ public static function renderAttributes($htmlOptions) { if(isset($specialAttributes[$name])) { - if($value) + if($value===false && $name==='async') { + $html .= ' ' . $name.'="false"'; + } + elseif($value) { $html .= ' ' . $name; if(self::$renderSpecialAttributesValue) diff --git a/framework/web/helpers/CJSON.php b/framework/web/helpers/CJSON.php index aa17b02448a..1d8f3d658c4 100644 --- a/framework/web/helpers/CJSON.php +++ b/framework/web/helpers/CJSON.php @@ -251,7 +251,15 @@ public static function encode($var) return '[' . join(',', array_map(array('CJSON', 'encode'), $var)) . ']'; case 'object': - if ($var instanceof Traversable) + // Check for the JsonSerializable interface available in PHP5.4 + // Note that instanceof returns false in case it doesnt know the interface. + if (interface_exists('JsonSerializable', false) && $var instanceof JsonSerializable) + { + // We use the function defined in the interface instead of json_encode. + // This way even for PHP < 5.4 one could define the interface and use it. + return self::encode($var->jsonSerialize()); + } + elseif ($var instanceof Traversable) { $vars = array(); foreach ($var as $k=>$v) diff --git a/framework/web/js/source/jquery.autocomplete.js b/framework/web/js/source/jquery.autocomplete.js index 324b1042e2c..a42a0f9efeb 100644 --- a/framework/web/js/source/jquery.autocomplete.js +++ b/framework/web/js/source/jquery.autocomplete.js @@ -4,6 +4,7 @@ * Modified for Yii Framework: * - Renamed "autocomplete" to "legacyautocomplete". * - Fixed IE8 problems (mario.ffranco). + * - Fixed compatibility for jQuery 1.9+ (.browser is deprecated) * * Copyright (c) 2009 Jörn Zaefferer * @@ -84,7 +85,7 @@ $.Autocompleter = function(input, options) { var blockSubmit; // prevent form submit in opera when selecting with return key - $.browser.opera && $(input.form).bind("submit.autocomplete", function() { + (navigator.userAgent.match(/OPERA|OPR\//i) !== null) && $(input.form).bind("submit.autocomplete", function() { if (blockSubmit) { blockSubmit = false; return false; @@ -92,7 +93,7 @@ $.Autocompleter = function(input, options) { }); // only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all - $input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) { + $input.bind(((navigator.userAgent.match(/OPERA|OPR\//i) !== null) ? "keypress" : "keydown") + ".autocomplete", function(event) { // a keypress means the input has focus // avoids issue where input had focus before the autocomplete was applied hasFocus = 1; @@ -738,7 +739,7 @@ $.Autocompleter.Select = function (options, input, select, config) { overflow: 'auto' }); - if($.browser.msie && typeof document.body.style.maxHeight === "undefined") { + if(navigator.userAgent.match(/MSIE/i) !== null && typeof document.body.style.maxHeight === "undefined") { var listHeight = 0; listItems.each(function() { listHeight += this.offsetHeight; diff --git a/framework/web/js/source/jquery.bgiframe.js b/framework/web/js/source/jquery.bgiframe.js index 5cd38bb1d15..f8be0b588d6 100644 --- a/framework/web/js/source/jquery.bgiframe.js +++ b/framework/web/js/source/jquery.bgiframe.js @@ -1,39 +1,69 @@ -/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net) +/*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh) * Licensed under the MIT License (LICENSE.txt). * - * Version 2.1.2 + * Version 3.0.1 + * + * Requires jQuery >= 1.2.6 */ -(function($){ - -$.fn.bgiframe = ($.browser.msie && /msie 6\.0/i.test(navigator.userAgent) ? function(s) { - s = $.extend({ - top : 'auto', // auto == .currentStyle.borderTopWidth - left : 'auto', // auto == .currentStyle.borderLeftWidth - width : 'auto', // auto == offsetWidth - height : 'auto', // auto == offsetHeight - opacity : true, - src : 'javascript:false;' - }, s); - var html = '':""),e._keyEvent=!1,B},_generateMonthYearHeader:function(e,t,n,r,i,s,o,u){var a=this._get(e,"changeMonth"),f=this._get(e,"changeYear"),l=this._get(e,"showMonthAfterYear"),c='
    ',h="";if(s||!a)h+=''+o[t]+"";else{var p=r&&r.getFullYear()==n,d=i&&i.getFullYear()==n;h+='"}l||(c+=h+(s||!a||!f?" ":""));if(!e.yearshtml){e.yearshtml="";if(s||!f)c+=''+n+"";else{var m=this._get(e,"yearRange").split(":"),g=(new Date).getFullYear(),y=function(e){var t=e.match(/c[+-].*/)?n+parseInt(e.substring(1),10):e.match(/[+-].*/)?g+parseInt(e,10):parseInt(e,10);return isNaN(t)?g:t},b=y(m[0]),w=Math.max(b,y(m[1]||""));b=r?Math.max(b,r.getFullYear()):b,w=i?Math.min(w,i.getFullYear()):w,e.yearshtml+='",c+=e.yearshtml,e.yearshtml=null}}return c+=this._get(e,"yearSuffix"),l&&(c+=(s||!a||!f?" ":"")+h),c+="
    ",c},_adjustInstDate:function(e,t,n){var r=e.drawYear+(n=="Y"?t:0),i=e.drawMonth+(n=="M"?t:0),s=Math.min(e.selectedDay,this._getDaysInMonth(r,i))+(n=="D"?t:0),o=this._restrictMinMax(e,this._daylightSavingAdjust(new Date(r,i,s)));e.selectedDay=o.getDate(),e.drawMonth=e.selectedMonth=o.getMonth(),e.drawYear=e.selectedYear=o.getFullYear(),(n=="M"||n=="Y")&&this._notifyChange(e)},_restrictMinMax:function(e,t){var n=this._getMinMaxDate(e,"min"),r=this._getMinMaxDate(e,"max"),i=n&&tr?r:i,i},_notifyChange:function(e){var t=this._get(e,"onChangeMonthYear");t&&t.apply(e.input?e.input[0]:null,[e.selectedYear,e.selectedMonth+1,e])},_getNumberOfMonths:function(e){var t=this._get(e,"numberOfMonths");return t==null?[1,1]:typeof t=="number"?[1,t]:t},_getMinMaxDate:function(e,t){return this._determineDate(e,this._get(e,t+"Date"),null)},_getDaysInMonth:function(e,t){return 32-this._daylightSavingAdjust(new Date(e,t,32)).getDate()},_getFirstDayOfMonth:function(e,t){return(new Date(e,t,1)).getDay()},_canAdjustMonth:function(e,t,n,r){var i=this._getNumberOfMonths(e),s=this._daylightSavingAdjust(new Date(n,r+(t<0?t:i[0]*i[1]),1));return t<0&&s.setDate(this._getDaysInMonth(s.getFullYear(),s.getMonth())),this._isInRange(e,s)},_isInRange:function(e,t){var n=this._getMinMaxDate(e,"min"),r=this._getMinMaxDate(e,"max");return(!n||t.getTime()>=n.getTime())&&(!r||t.getTime()<=r.getTime())},_getFormatConfig:function(e){var t=this._get(e,"shortYearCutoff");return t=typeof t!="string"?t:(new Date).getFullYear()%100+parseInt(t,10),{shortYearCutoff:t,dayNamesShort:this._get(e,"dayNamesShort"),dayNames:this._get(e,"dayNames"),monthNamesShort:this._get(e,"monthNamesShort"),monthNames:this._get(e,"monthNames")}},_formatDate:function(e,t,n,r){t||(e.currentDay=e.selectedDay,e.currentMonth=e.selectedMonth,e.currentYear=e.selectedYear);var i=t?typeof t=="object"?t:this._daylightSavingAdjust(new Date(r,n,t)):this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return this.formatDate(this._get(e,"dateFormat"),i,this._getFormatConfig(e))}}),$.fn.datepicker=function(e){if(!this.length)return this;$.datepicker.initialized||($(document).mousedown($.datepicker._checkExternalClick).find(document.body).append($.datepicker.dpDiv),$.datepicker.initialized=!0);var t=Array.prototype.slice.call(arguments,1);return typeof e!="string"||e!="isDisabled"&&e!="getDate"&&e!="widget"?e=="option"&&arguments.length==2&&typeof arguments[1]=="string"?$.datepicker["_"+e+"Datepicker"].apply($.datepicker,[this[0]].concat(t)):this.each(function(){typeof e=="string"?$.datepicker["_"+e+"Datepicker"].apply($.datepicker,[this].concat(t)):$.datepicker._attachDatepicker(this,e)}):$.datepicker["_"+e+"Datepicker"].apply($.datepicker,[this[0]].concat(t))},$.datepicker=new Datepicker,$.datepicker.initialized=!1,$.datepicker.uuid=(new Date).getTime(),$.datepicker.version="1.9.2",window["DP_jQuery_"+dpuuid]=$})(jQuery);(function(e,t){var n="ui-dialog ui-widget ui-widget-content ui-corner-all ",r={buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},i={maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0};e.widget("ui.dialog",{version:"1.9.2",options:{autoOpen:!0,buttons:{},closeOnEscape:!0,closeText:"close",dialogClass:"",draggable:!0,hide:null,height:"auto",maxHeight:!1,maxWidth:!1,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(t){var n=e(this).css(t).offset().top;n<0&&e(this).css("top",t.top-n)}},resizable:!0,show:null,stack:!0,title:"",width:300,zIndex:1e3},_create:function(){this.originalTitle=this.element.attr("title"),typeof this.originalTitle!="string"&&(this.originalTitle=""),this.oldPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.options.title=this.options.title||this.originalTitle;var t=this,r=this.options,i=r.title||" ",s,o,u,a,f;s=(this.uiDialog=e("
    ")).addClass(n+r.dialogClass).css({display:"none",outline:0,zIndex:r.zIndex}).attr("tabIndex",-1).keydown(function(n){r.closeOnEscape&&!n.isDefaultPrevented()&&n.keyCode&&n.keyCode===e.ui.keyCode.ESCAPE&&(t.close(n),n.preventDefault())}).mousedown(function(e){t.moveToTop(!1,e)}).appendTo("body"),this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(s),o=(this.uiDialogTitlebar=e("
    ")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").bind("mousedown",function(){s.focus()}).prependTo(s),u=e("").addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").click(function(e){e.preventDefault(),t.close(e)}).appendTo(o),(this.uiDialogTitlebarCloseText=e("")).addClass("ui-icon ui-icon-closethick").text(r.closeText).appendTo(u),a=e("").uniqueId().addClass("ui-dialog-title").html(i).prependTo(o),f=(this.uiDialogButtonPane=e("
    ")).addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),(this.uiButtonSet=e("
    ")).addClass("ui-dialog-buttonset").appendTo(f),s.attr({role:"dialog","aria-labelledby":a.attr("id")}),o.find("*").add(o).disableSelection(),this._hoverable(u),this._focusable(u),r.draggable&&e.fn.draggable&&this._makeDraggable(),r.resizable&&e.fn.resizable&&this._makeResizable(),this._createButtons(r.buttons),this._isOpen=!1,e.fn.bgiframe&&s.bgiframe(),this._on(s,{keydown:function(t){if(!r.modal||t.keyCode!==e.ui.keyCode.TAB)return;var n=e(":tabbable",s),i=n.filter(":first"),o=n.filter(":last");if(t.target===o[0]&&!t.shiftKey)return i.focus(1),!1;if(t.target===i[0]&&t.shiftKey)return o.focus(1),!1}})},_init:function(){this.options.autoOpen&&this.open()},_destroy:function(){var e,t=this.oldPosition;this.overlay&&this.overlay.destroy(),this.uiDialog.hide(),this.element.removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body"),this.uiDialog.remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),e=t.parent.children().eq(t.index),e.length&&e[0]!==this.element[0]?e.before(this.element):t.parent.append(this.element)},widget:function(){return this.uiDialog},close:function(t){var n=this,r,i;if(!this._isOpen)return;if(!1===this._trigger("beforeClose",t))return;return this._isOpen=!1,this.overlay&&this.overlay.destroy(),this.options.hide?this._hide(this.uiDialog,this.options.hide,function(){n._trigger("close",t)}):(this.uiDialog.hide(),this._trigger("close",t)),e.ui.dialog.overlay.resize(),this.options.modal&&(r=0,e(".ui-dialog").each(function(){this!==n.uiDialog[0]&&(i=e(this).css("z-index"),isNaN(i)||(r=Math.max(r,i)))}),e.ui.dialog.maxZ=r),this},isOpen:function(){return this._isOpen},moveToTop:function(t,n){var r=this.options,i;return r.modal&&!t||!r.stack&&!r.modal?this._trigger("focus",n):(r.zIndex>e.ui.dialog.maxZ&&(e.ui.dialog.maxZ=r.zIndex),this.overlay&&(e.ui.dialog.maxZ+=1,e.ui.dialog.overlay.maxZ=e.ui.dialog.maxZ,this.overlay.$el.css("z-index",e.ui.dialog.overlay.maxZ)),i={scrollTop:this.element.scrollTop(),scrollLeft:this.element.scrollLeft()},e.ui.dialog.maxZ+=1,this.uiDialog.css("z-index",e.ui.dialog.maxZ),this.element.attr(i),this._trigger("focus",n),this)},open:function(){if(this._isOpen)return;var t,n=this.options,r=this.uiDialog;return this._size(),this._position(n.position),r.show(n.show),this.overlay=n.modal?new e.ui.dialog.overlay(this):null,this.moveToTop(!0),t=this.element.find(":tabbable"),t.length||(t=this.uiDialogButtonPane.find(":tabbable"),t.length||(t=r)),t.eq(0).focus(),this._isOpen=!0,this._trigger("open"),this},_createButtons:function(t){var n=this,r=!1;this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),typeof t=="object"&&t!==null&&e.each(t,function(){return!(r=!0)}),r?(e.each(t,function(t,r){var i,s;r=e.isFunction(r)?{click:r,text:t}:r,r=e.extend({type:"button"},r),s=r.click,r.click=function(){s.apply(n.element[0],arguments)},i=e("",r).appendTo(n.uiButtonSet),e.fn.button&&i.button()}),this.uiDialog.addClass("ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog)):this.uiDialog.removeClass("ui-dialog-buttons")},_makeDraggable:function(){function r(e){return{position:e.position,offset:e.offset}}var t=this,n=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(n,i){e(this).addClass("ui-dialog-dragging"),t._trigger("dragStart",n,r(i))},drag:function(e,n){t._trigger("drag",e,r(n))},stop:function(i,s){n.position=[s.position.left-t.document.scrollLeft(),s.position.top-t.document.scrollTop()],e(this).removeClass("ui-dialog-dragging"),t._trigger("dragStop",i,r(s)),e.ui.dialog.overlay.resize()}})},_makeResizable:function(n){function u(e){return{originalPosition:e.originalPosition,originalSize:e.originalSize,position:e.position,size:e.size}}n=n===t?this.options.resizable:n;var r=this,i=this.options,s=this.uiDialog.css("position"),o=typeof n=="string"?n:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:i.maxWidth,maxHeight:i.maxHeight,minWidth:i.minWidth,minHeight:this._minHeight(),handles:o,start:function(t,n){e(this).addClass("ui-dialog-resizing"),r._trigger("resizeStart",t,u(n))},resize:function(e,t){r._trigger("resize",e,u(t))},stop:function(t,n){e(this).removeClass("ui-dialog-resizing"),i.height=e(this).height(),i.width=e(this).width(),r._trigger("resizeStop",t,u(n)),e.ui.dialog.overlay.resize()}}).css("position",s).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var e=this.options;return e.height==="auto"?e.minHeight:Math.min(e.minHeight,e.height)},_position:function(t){var n=[],r=[0,0],i;if(t){if(typeof t=="string"||typeof t=="object"&&"0"in t)n=t.split?t.split(" "):[t[0],t[1]],n.length===1&&(n[1]=n[0]),e.each(["left","top"],function(e,t){+n[e]===n[e]&&(r[e]=n[e],n[e]=t)}),t={my:n[0]+(r[0]<0?r[0]:"+"+r[0])+" "+n[1]+(r[1]<0?r[1]:"+"+r[1]),at:n.join(" ")};t=e.extend({},e.ui.dialog.prototype.options.position,t)}else t=e.ui.dialog.prototype.options.position;i=this.uiDialog.is(":visible"),i||this.uiDialog.show(),this.uiDialog.position(t),i||this.uiDialog.hide()},_setOptions:function(t){var n=this,s={},o=!1;e.each(t,function(e,t){n._setOption(e,t),e in r&&(o=!0),e in i&&(s[e]=t)}),o&&this._size(),this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",s)},_setOption:function(t,r){var i,s,o=this.uiDialog;switch(t){case"buttons":this._createButtons(r);break;case"closeText":this.uiDialogTitlebarCloseText.text(""+r);break;case"dialogClass":o.removeClass(this.options.dialogClass).addClass(n+r);break;case"disabled":r?o.addClass("ui-dialog-disabled"):o.removeClass("ui-dialog-disabled");break;case"draggable":i=o.is(":data(draggable)"),i&&!r&&o.draggable("destroy"),!i&&r&&this._makeDraggable();break;case"position":this._position(r);break;case"resizable":s=o.is(":data(resizable)"),s&&!r&&o.resizable("destroy"),s&&typeof r=="string"&&o.resizable("option","handles",r),!s&&r!==!1&&this._makeResizable(r);break;case"title":e(".ui-dialog-title",this.uiDialogTitlebar).html(""+(r||" "))}this._super(t,r)},_size:function(){var t,n,r,i=this.options,s=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0}),i.minWidth>i.width&&(i.width=i.minWidth),t=this.uiDialog.css({height:"auto",width:i.width}).outerHeight(),n=Math.max(0,i.minHeight-t),i.height==="auto"?e.support.minHeight?this.element.css({minHeight:n,height:"auto"}):(this.uiDialog.show(),r=this.element.css("height","auto").height(),s||this.uiDialog.hide(),this.element.height(Math.max(r,n))):this.element.height(Math.max(i.height-t,0)),this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}}),e.extend(e.ui.dialog,{uuid:0,maxZ:0,getTitleId:function(e){var t=e.attr("id");return t||(this.uuid+=1,t=this.uuid),"ui-dialog-title-"+t},overlay:function(t){this.$el=e.ui.dialog.overlay.create(t)}}),e.extend(e.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:e.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(e){return e+".dialog-overlay"}).join(" "),create:function(t){this.instances.length===0&&(setTimeout(function(){e.ui.dialog.overlay.instances.length&&e(document).bind(e.ui.dialog.overlay.events,function(t){if(e(t.target).zIndex()").addClass("ui-widget-overlay");return e(document).bind("keydown.dialog-overlay",function(r){var i=e.ui.dialog.overlay.instances;i.length!==0&&i[i.length-1]===n&&t.options.closeOnEscape&&!r.isDefaultPrevented()&&r.keyCode&&r.keyCode===e.ui.keyCode.ESCAPE&&(t.close(r),r.preventDefault())}),n.appendTo(document.body).css({width:this.width(),height:this.height()}),e.fn.bgiframe&&n.bgiframe(),this.instances.push(n),n},destroy:function(t){var n=e.inArray(t,this.instances),r=0;n!==-1&&this.oldInstances.push(this.instances.splice(n,1)[0]),this.instances.length===0&&e([document,window]).unbind(".dialog-overlay"),t.height(0).width(0).remove(),e.each(this.instances,function(){r=Math.max(r,this.css("z-index"))}),this.maxZ=r},height:function(){var t,n;return e.ui.ie?(t=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight),n=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight),t
    ').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(e(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(t){var n=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,n.cursorAt&&this._adjustOffsetFromHelper(n.cursorAt),n.containment&&this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!n.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_mouseDrag:function(t,n){this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute");if(!n){var r=this._uiHash();if(this._trigger("drag",t,r)===!1)return this._mouseUp({}),!1;this.position=r.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";return e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var n=!1;e.ui.ddmanager&&!this.options.dropBehaviour&&(n=e.ui.ddmanager.drop(this,t)),this.dropped&&(n=this.dropped,this.dropped=!1);var r=this.element[0],i=!1;while(r&&(r=r.parentNode))r==document&&(i=!0);if(!i&&this.options.helper==="original")return!1;if(this.options.revert=="invalid"&&!n||this.options.revert=="valid"&&n||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,n)){var s=this;e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){s._trigger("stop",t)!==!1&&s._clear()})}else this._trigger("stop",t)!==!1&&this._clear();return!1},_mouseUp:function(t){return e("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){var n=!this.options.handle||!e(this.options.handle,this.element).length?!0:!1;return e(this.options.handle,this.element).find("*").andSelf().each(function(){this==t.target&&(n=!0)}),n},_createHelper:function(t){var n=this.options,r=e.isFunction(n.helper)?e(n.helper.apply(this.element[0],[t])):n.helper=="clone"?this.element.clone().removeAttr("id"):this.element;return r.parents("body").length||r.appendTo(n.appendTo=="parent"?this.element[0].parentNode:n.appendTo),r[0]!=this.element[0]&&!/(fixed|absolute)/.test(r.css("position"))&&r.css("position","absolute"),r},_adjustOffsetFromHelper:function(t){typeof t=="string"&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var t=this.offsetParent.offset();this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop());if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&e.ui.ie)t={top:0,left:0};return{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var e=this.element.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t=this.options;t.containment=="parent"&&(t.containment=this.helper[0].parentNode);if(t.containment=="document"||t.containment=="window")this.containment=[t.containment=="document"?0:e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t.containment=="document"?0:e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,(t.containment=="document"?0:e(window).scrollLeft())+e(t.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(t.containment=="document"?0:e(window).scrollTop())+(e(t.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(t.containment)&&t.containment.constructor!=Array){var n=e(t.containment),r=n[0];if(!r)return;var i=n.offset(),s=e(r).css("overflow")!="hidden";this.containment=[(parseInt(e(r).css("borderLeftWidth"),10)||0)+(parseInt(e(r).css("paddingLeft"),10)||0),(parseInt(e(r).css("borderTopWidth"),10)||0)+(parseInt(e(r).css("paddingTop"),10)||0),(s?Math.max(r.scrollWidth,r.offsetWidth):r.offsetWidth)-(parseInt(e(r).css("borderLeftWidth"),10)||0)-(parseInt(e(r).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(s?Math.max(r.scrollHeight,r.offsetHeight):r.offsetHeight)-(parseInt(e(r).css("borderTopWidth"),10)||0)-(parseInt(e(r).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=n}else t.containment.constructor==Array&&(this.containment=t.containment)},_convertPositionTo:function(t,n){n||(n=this.position);var r=t=="absolute"?1:-1,i=this.options,s=this.cssPosition!="absolute"||this.scrollParent[0]!=document&&!!e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(s[0].tagName);return{top:n.top+this.offset.relative.top*r+this.offset.parent.top*r-(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():o?0:s.scrollTop())*r,left:n.left+this.offset.relative.left*r+this.offset.parent.left*r-(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():o?0:s.scrollLeft())*r}},_generatePosition:function(t){var n=this.options,r=this.cssPosition!="absolute"||this.scrollParent[0]!=document&&!!e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,i=/(html|body)/i.test(r[0].tagName),s=t.pageX,o=t.pageY;if(this.originalPosition){var u;if(this.containment){if(this.relative_container){var a=this.relative_container.offset();u=[this.containment[0]+a.left,this.containment[1]+a.top,this.containment[2]+a.left,this.containment[3]+a.top]}else u=this.containment;t.pageX-this.offset.click.leftu[2]&&(s=u[2]+this.offset.click.left),t.pageY-this.offset.click.top>u[3]&&(o=u[3]+this.offset.click.top)}if(n.grid){var f=n.grid[1]?this.originalPageY+Math.round((o-this.originalPageY)/n.grid[1])*n.grid[1]:this.originalPageY;o=u?f-this.offset.click.topu[3]?f-this.offset.click.topu[2]?l-this.offset.click.left=0;l--){var c=r.snapElements[l].left,h=c+r.snapElements[l].width,p=r.snapElements[l].top,d=p+r.snapElements[l].height;if(!(c-s=l&&o<=c||u>=l&&u<=c||oc)&&(i>=a&&i<=f||s>=a&&s<=f||if);default:return!1}},e.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(t,n){var r=e.ui.ddmanager.droppables[t.options.scope]||[],i=n?n.type:null,s=(t.currentItem||t.element).find(":data(droppable)").andSelf();e:for(var o=0;oe?0:r.max")[0],c,h=t.each;l.style.cssText="background-color:rgba(1,1,1,.5)",f.rgba=l.style.backgroundColor.indexOf("rgba")>-1,h(u,function(e,t){t.cache="_"+e,t.props.alpha={idx:3,type:"percent",def:1}}),o.fn=t.extend(o.prototype,{parse:function(r,i,s,a){if(r===n)return this._rgba=[null,null,null,null],this;if(r.jquery||r.nodeType)r=t(r).css(i),i=n;var f=this,l=t.type(r),v=this._rgba=[];i!==n&&(r=[r,i,s,a],l="array");if(l==="string")return this.parse(d(r)||c._default);if(l==="array")return h(u.rgba.props,function(e,t){v[t.idx]=p(r[t.idx],t)}),this;if(l==="object")return r instanceof o?h(u,function(e,t){r[t.cache]&&(f[t.cache]=r[t.cache].slice())}):h(u,function(t,n){var i=n.cache;h(n.props,function(e,t){if(!f[i]&&n.to){if(e==="alpha"||r[e]==null)return;f[i]=n.to(f._rgba)}f[i][t.idx]=p(r[e],t,!0)}),f[i]&&e.inArray(null,f[i].slice(0,3))<0&&(f[i][3]=1,n.from&&(f._rgba=n.from(f[i])))}),this},is:function(e){var t=o(e),n=!0,r=this;return h(u,function(e,i){var s,o=t[i.cache];return o&&(s=r[i.cache]||i.to&&i.to(r._rgba)||[],h(i.props,function(e,t){if(o[t.idx]!=null)return n=o[t.idx]===s[t.idx],n})),n}),n},_space:function(){var e=[],t=this;return h(u,function(n,r){t[r.cache]&&e.push(n)}),e.pop()},transition:function(e,t){var n=o(e),r=n._space(),i=u[r],s=this.alpha()===0?o("transparent"):this,f=s[i.cache]||i.to(s._rgba),l=f.slice();return n=n[i.cache],h(i.props,function(e,r){var i=r.idx,s=f[i],o=n[i],u=a[r.type]||{};if(o===null)return;s===null?l[i]=o:(u.mod&&(o-s>u.mod/2?s+=u.mod:s-o>u.mod/2&&(s-=u.mod)),l[i]=p((o-s)*t+s,r))}),this[r](l)},blend:function(e){if(this._rgba[3]===1)return this;var n=this._rgba.slice(),r=n.pop(),i=o(e)._rgba;return o(t.map(n,function(e,t){return(1-r)*i[t]+r*e}))},toRgbaString:function(){var e="rgba(",n=t.map(this._rgba,function(e,t){return e==null?t>2?1:0:e});return n[3]===1&&(n.pop(),e="rgb("),e+n.join()+")"},toHslaString:function(){var e="hsla(",n=t.map(this.hsla(),function(e,t){return e==null&&(e=t>2?1:0),t&&t<3&&(e=Math.round(e*100)+"%"),e});return n[3]===1&&(n.pop(),e="hsl("),e+n.join()+")"},toHexString:function(e){var n=this._rgba.slice(),r=n.pop();return e&&n.push(~~(r*255)),"#"+t.map(n,function(e){return e=(e||0).toString(16),e.length===1?"0"+e:e}).join("")},toString:function(){return this._rgba[3]===0?"transparent":this.toRgbaString()}}),o.fn.parse.prototype=o.fn,u.hsla.to=function(e){if(e[0]==null||e[1]==null||e[2]==null)return[null,null,null,e[3]];var t=e[0]/255,n=e[1]/255,r=e[2]/255,i=e[3],s=Math.max(t,n,r),o=Math.min(t,n,r),u=s-o,a=s+o,f=a*.5,l,c;return o===s?l=0:t===s?l=60*(n-r)/u+360:n===s?l=60*(r-t)/u+120:l=60*(t-n)/u+240,f===0||f===1?c=f:f<=.5?c=u/a:c=u/(2-a),[Math.round(l)%360,c,f,i==null?1:i]},u.hsla.from=function(e){if(e[0]==null||e[1]==null||e[2]==null)return[null,null,null,e[3]];var t=e[0]/360,n=e[1],r=e[2],i=e[3],s=r<=.5?r*(1+n):r+n-r*n,o=2*r-s;return[Math.round(v(o,s,t+1/3)*255),Math.round(v(o,s,t)*255),Math.round(v(o,s,t-1/3)*255),i]},h(u,function(e,r){var s=r.props,u=r.cache,a=r.to,f=r.from;o.fn[e]=function(e){a&&!this[u]&&(this[u]=a(this._rgba));if(e===n)return this[u].slice();var r,i=t.type(e),l=i==="array"||i==="object"?e:arguments,c=this[u].slice();return h(s,function(e,t){var n=l[i==="object"?e:t.idx];n==null&&(n=c[t.idx]),c[t.idx]=p(n,t)}),f?(r=o(f(c)),r[u]=c,r):o(c)},h(s,function(n,r){if(o.fn[n])return;o.fn[n]=function(s){var o=t.type(s),u=n==="alpha"?this._hsla?"hsla":"rgba":e,a=this[u](),f=a[r.idx],l;return o==="undefined"?f:(o==="function"&&(s=s.call(this,f),o=t.type(s)),s==null&&r.empty?this:(o==="string"&&(l=i.exec(s),l&&(s=f+parseFloat(l[2])*(l[1]==="+"?1:-1))),a[r.idx]=s,this[u](a)))}})}),h(r,function(e,n){t.cssHooks[n]={set:function(e,r){var i,s,u="";if(t.type(r)!=="string"||(i=d(r))){r=o(i||r);if(!f.rgba&&r._rgba[3]!==1){s=n==="backgroundColor"?e.parentNode:e;while((u===""||u==="transparent")&&s&&s.style)try{u=t.css(s,"backgroundColor"),s=s.parentNode}catch(a){}r=r.blend(u&&u!=="transparent"?u:"_default")}r=r.toRgbaString()}try{e.style[n]=r}catch(l){}}},t.fx.step[n]=function(e){e.colorInit||(e.start=o(e.elem,n),e.end=o(e.end),e.colorInit=!0),t.cssHooks[n].set(e.elem,e.start.transition(e.end,e.pos))}}),t.cssHooks.borderColor={expand:function(e){var t={};return h(["Top","Right","Bottom","Left"],function(n,r){t["border"+r+"Color"]=e}),t}},c=t.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(jQuery),function(){function i(){var t=this.ownerDocument.defaultView?this.ownerDocument.defaultView.getComputedStyle(this,null):this.currentStyle,n={},r,i;if(t&&t.length&&t[0]&&t[t[0]]){i=t.length;while(i--)r=t[i],typeof t[r]=="string"&&(n[e.camelCase(r)]=t[r])}else for(r in t)typeof t[r]=="string"&&(n[r]=t[r]);return n}function s(t,n){var i={},s,o;for(s in n)o=n[s],t[s]!==o&&!r[s]&&(e.fx.step[s]||!isNaN(parseFloat(o)))&&(i[s]=o);return i}var n=["add","remove","toggle"],r={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};e.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(t,n){e.fx.step[n]=function(e){if(e.end!=="none"&&!e.setAttr||e.pos===1&&!e.setAttr)jQuery.style(e.elem,n,e.end),e.setAttr=!0}}),e.effects.animateClass=function(t,r,o,u){var a=e.speed(r,o,u);return this.queue(function(){var r=e(this),o=r.attr("class")||"",u,f=a.children?r.find("*").andSelf():r;f=f.map(function(){var t=e(this);return{el:t,start:i.call(this)}}),u=function(){e.each(n,function(e,n){t[n]&&r[n+"Class"](t[n])})},u(),f=f.map(function(){return this.end=i.call(this.el[0]),this.diff=s(this.start,this.end),this}),r.attr("class",o),f=f.map(function(){var t=this,n=e.Deferred(),r=jQuery.extend({},a,{queue:!1,complete:function(){n.resolve(t)}});return this.el.animate(this.diff,r),n.promise()}),e.when.apply(e,f.get()).done(function(){u(),e.each(arguments,function(){var t=this.el;e.each(this.diff,function(e){t.css(e,"")})}),a.complete.call(r[0])})})},e.fn.extend({_addClass:e.fn.addClass,addClass:function(t,n,r,i){return n?e.effects.animateClass.call(this,{add:t},n,r,i):this._addClass(t)},_removeClass:e.fn.removeClass,removeClass:function(t,n,r,i){return n?e.effects.animateClass.call(this,{remove:t},n,r,i):this._removeClass(t)},_toggleClass:e.fn.toggleClass,toggleClass:function(n,r,i,s,o){return typeof r=="boolean"||r===t?i?e.effects.animateClass.call(this,r?{add:n}:{remove:n},i,s,o):this._toggleClass(n,r):e.effects.animateClass.call(this,{toggle:n},r,i,s)},switchClass:function(t,n,r,i,s){return e.effects.animateClass.call(this,{add:n,remove:t},r,i,s)}})}(),function(){function i(t,n,r,i){e.isPlainObject(t)&&(n=t,t=t.effect),t={effect:t},n==null&&(n={}),e.isFunction(n)&&(i=n,r=null,n={});if(typeof n=="number"||e.fx.speeds[n])i=r,r=n,n={};return e.isFunction(r)&&(i=r,r=null),n&&e.extend(t,n),r=r||n.duration,t.duration=e.fx.off?0:typeof r=="number"?r:r in e.fx.speeds?e.fx.speeds[r]:e.fx.speeds._default,t.complete=i||n.complete,t}function s(t){return!t||typeof t=="number"||e.fx.speeds[t]?!0:typeof t=="string"&&!e.effects.effect[t]?n&&e.effects[t]?!1:!0:!1}e.extend(e.effects,{version:"1.9.2",save:function(e,t){for(var n=0;n
    ").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),i={width:t.width(),height:t.height()},s=document.activeElement;try{s.id}catch(o){s=document.body}return t.wrap(r),(t[0]===s||e.contains(t[0],s))&&e(s).focus(),r=t.parent(),t.css("position")==="static"?(r.css({position:"relative"}),t.css({position:"relative"})):(e.extend(n,{position:t.css("position"),zIndex:t.css("z-index")}),e.each(["top","left","bottom","right"],function(e,r){n[r]=t.css(r),isNaN(parseInt(n[r],10))&&(n[r]="auto")}),t.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),t.css(i),r.css(n).show()},removeWrapper:function(t){var n=document.activeElement;return t.parent().is(".ui-effects-wrapper")&&(t.parent().replaceWith(t),(t[0]===n||e.contains(t[0],n))&&e(n).focus()),t},setTransition:function(t,n,r,i){return i=i||{},e.each(n,function(e,n){var s=t.cssUnit(n);s[0]>0&&(i[n]=s[0]*r+s[1])}),i}}),e.fn.extend({effect:function(){function a(n){function u(){e.isFunction(i)&&i.call(r[0]),e.isFunction(n)&&n()}var r=e(this),i=t.complete,s=t.mode;(r.is(":hidden")?s==="hide":s==="show")?u():o.call(r[0],t,u)}var t=i.apply(this,arguments),r=t.mode,s=t.queue,o=e.effects.effect[t.effect],u=!o&&n&&e.effects[t.effect];return e.fx.off||!o&&!u?r?this[r](t.duration,t.complete):this.each(function(){t.complete&&t.complete.call(this)}):o?s===!1?this.each(a):this.queue(s||"fx",a):u.call(this,{options:t,duration:t.duration,callback:t.complete,mode:t.mode})},_show:e.fn.show,show:function(e){if(s(e))return this._show.apply(this,arguments);var t=i.apply(this,arguments);return t.mode="show",this.effect.call(this,t)},_hide:e.fn.hide,hide:function(e){if(s(e))return this._hide.apply(this,arguments);var t=i.apply(this,arguments);return t.mode="hide",this.effect.call(this,t)},__toggle:e.fn.toggle,toggle:function(t){if(s(t)||typeof t=="boolean"||e.isFunction(t))return this.__toggle.apply(this,arguments);var n=i.apply(this,arguments);return n.mode="toggle",this.effect.call(this,n)},cssUnit:function(t){var n=this.css(t),r=[];return e.each(["em","px","%","pt"],function(e,t){n.indexOf(t)>0&&(r=[parseFloat(n),t])}),r}})}(),function(){var t={};e.each(["Quad","Cubic","Quart","Quint","Expo"],function(e,n){t[n]=function(t){return Math.pow(t,e+2)}}),e.extend(t,{Sine:function(e){return 1-Math.cos(e*Math.PI/2)},Circ:function(e){return 1-Math.sqrt(1-e*e)},Elastic:function(e){return e===0||e===1?e:-Math.pow(2,8*(e-1))*Math.sin(((e-1)*80-7.5)*Math.PI/15)},Back:function(e){return e*e*(3*e-2)},Bounce:function(e){var t,n=4;while(e<((t=Math.pow(2,--n))-1)/11);return 1/Math.pow(4,3-n)-7.5625*Math.pow((t*3-2)/22-e,2)}}),e.each(t,function(t,n){e.easing["easeIn"+t]=n,e.easing["easeOut"+t]=function(e){return 1-n(1-e)},e.easing["easeInOut"+t]=function(e){return e<.5?n(e*2)/2:1-n(e*-2+2)/2}})}()}(jQuery);(function(e,t){var n=/up|down|vertical/,r=/up|left|vertical|horizontal/;e.effects.effect.blind=function(t,i){var s=e(this),o=["position","top","bottom","left","right","height","width"],u=e.effects.setMode(s,t.mode||"hide"),a=t.direction||"up",f=n.test(a),l=f?"height":"width",c=f?"top":"left",h=r.test(a),p={},d=u==="show",v,m,g;s.parent().is(".ui-effects-wrapper")?e.effects.save(s.parent(),o):e.effects.save(s,o),s.show(),v=e.effects.createWrapper(s).css({overflow:"hidden"}),m=v[l](),g=parseFloat(v.css(c))||0,p[l]=d?m:0,h||(s.css(f?"bottom":"right",0).css(f?"top":"left","auto").css({position:"absolute"}),p[c]=d?g:m+g),d&&(v.css(l,0),h||v.css(c,g+m)),v.animate(p,{duration:t.duration,easing:t.easing,queue:!1,complete:function(){u==="hide"&&s.hide(),e.effects.restore(s,o),e.effects.removeWrapper(s),i()}})}})(jQuery);(function(e,t){e.effects.effect.bounce=function(t,n){var r=e(this),i=["position","top","bottom","left","right","height","width"],s=e.effects.setMode(r,t.mode||"effect"),o=s==="hide",u=s==="show",a=t.direction||"up",f=t.distance,l=t.times||5,c=l*2+(u||o?1:0),h=t.duration/c,p=t.easing,d=a==="up"||a==="down"?"top":"left",v=a==="up"||a==="left",m,g,y,b=r.queue(),w=b.length;(u||o)&&i.push("opacity"),e.effects.save(r,i),r.show(),e.effects.createWrapper(r),f||(f=r[d==="top"?"outerHeight":"outerWidth"]()/3),u&&(y={opacity:1},y[d]=0,r.css("opacity",0).css(d,v?-f*2:f*2).animate(y,h,p)),o&&(f/=Math.pow(2,l-1)),y={},y[d]=0;for(m=0;m1&&b.splice.apply(b,[1,0].concat(b.splice(w,c+1))),r.dequeue()}})(jQuery);(function(e,t){e.effects.effect.clip=function(t,n){var r=e(this),i=["position","top","bottom","left","right","height","width"],s=e.effects.setMode(r,t.mode||"hide"),o=s==="show",u=t.direction||"vertical",a=u==="vertical",f=a?"height":"width",l=a?"top":"left",c={},h,p,d;e.effects.save(r,i),r.show(),h=e.effects.createWrapper(r).css({overflow:"hidden"}),p=r[0].tagName==="IMG"?h:r,d=p[f](),o&&(p.css(f,0),p.css(l,d/2)),c[f]=o?d:0,c[l]=o?0:d/2,p.animate(c,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){o||r.hide(),e.effects.restore(r,i),e.effects.removeWrapper(r),n()}})}})(jQuery);(function(e,t){e.effects.effect.drop=function(t,n){var r=e(this),i=["position","top","bottom","left","right","opacity","height","width"],s=e.effects.setMode(r,t.mode||"hide"),o=s==="show",u=t.direction||"left",a=u==="up"||u==="down"?"top":"left",f=u==="up"||u==="left"?"pos":"neg",l={opacity:o?1:0},c;e.effects.save(r,i),r.show(),e.effects.createWrapper(r),c=t.distance||r[a==="top"?"outerHeight":"outerWidth"](!0)/2,o&&r.css("opacity",0).css(a,f==="pos"?-c:c),l[a]=(o?f==="pos"?"+=":"-=":f==="pos"?"-=":"+=")+c,r.animate(l,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){s==="hide"&&r.hide(),e.effects.restore(r,i),e.effects.removeWrapper(r),n()}})}})(jQuery);(function(e,t){e.effects.effect.explode=function(t,n){function y(){c.push(this),c.length===r*i&&b()}function b(){s.css({visibility:"visible"}),e(c).remove(),u||s.hide(),n()}var r=t.pieces?Math.round(Math.sqrt(t.pieces)):3,i=r,s=e(this),o=e.effects.setMode(s,t.mode||"hide"),u=o==="show",a=s.show().css("visibility","hidden").offset(),f=Math.ceil(s.outerWidth()/i),l=Math.ceil(s.outerHeight()/r),c=[],h,p,d,v,m,g;for(h=0;h
    ").css({position:"absolute",visibility:"visible",left:-p*f,top:-h*l}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:f,height:l,left:d+(u?m*f:0),top:v+(u?g*l:0),opacity:u?0:1}).animate({left:d+(u?0:m*f),top:v+(u?0:g*l),opacity:u?1:0},t.duration||500,t.easing,y)}}})(jQuery);(function(e,t){e.effects.effect.fade=function(t,n){var r=e(this),i=e.effects.setMode(r,t.mode||"toggle");r.animate({opacity:i},{queue:!1,duration:t.duration,easing:t.easing,complete:n})}})(jQuery);(function(e,t){e.effects.effect.fold=function(t,n){var r=e(this),i=["position","top","bottom","left","right","height","width"],s=e.effects.setMode(r,t.mode||"hide"),o=s==="show",u=s==="hide",a=t.size||15,f=/([0-9]+)%/.exec(a),l=!!t.horizFirst,c=o!==l,h=c?["width","height"]:["height","width"],p=t.duration/2,d,v,m={},g={};e.effects.save(r,i),r.show(),d=e.effects.createWrapper(r).css({overflow:"hidden"}),v=c?[d.width(),d.height()]:[d.height(),d.width()],f&&(a=parseInt(f[1],10)/100*v[u?0:1]),o&&d.css(l?{height:0,width:a}:{height:a,width:0}),m[h[0]]=o?v[0]:a,g[h[1]]=o?v[1]:0,d.animate(m,p,t.easing).animate(g,p,t.easing,function(){u&&r.hide(),e.effects.restore(r,i),e.effects.removeWrapper(r),n()})}})(jQuery);(function(e,t){e.effects.effect.highlight=function(t,n){var r=e(this),i=["backgroundImage","backgroundColor","opacity"],s=e.effects.setMode(r,t.mode||"show"),o={backgroundColor:r.css("backgroundColor")};s==="hide"&&(o.opacity=0),e.effects.save(r,i),r.show().css({backgroundImage:"none",backgroundColor:t.color||"#ffff99"}).animate(o,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){s==="hide"&&r.hide(),e.effects.restore(r,i),n()}})}})(jQuery);(function(e,t){e.effects.effect.pulsate=function(t,n){var r=e(this),i=e.effects.setMode(r,t.mode||"show"),s=i==="show",o=i==="hide",u=s||i==="hide",a=(t.times||5)*2+(u?1:0),f=t.duration/a,l=0,c=r.queue(),h=c.length,p;if(s||!r.is(":visible"))r.css("opacity",0).show(),l=1;for(p=1;p1&&c.splice.apply(c,[1,0].concat(c.splice(h,a+1))),r.dequeue()}})(jQuery);(function(e,t){e.effects.effect.puff=function(t,n){var r=e(this),i=e.effects.setMode(r,t.mode||"hide"),s=i==="hide",o=parseInt(t.percent,10)||150,u=o/100,a={height:r.height(),width:r.width(),outerHeight:r.outerHeight(),outerWidth:r.outerWidth()};e.extend(t,{effect:"scale",queue:!1,fade:!0,mode:i,complete:n,percent:s?o:100,from:s?a:{height:a.height*u,width:a.width*u,outerHeight:a.outerHeight*u,outerWidth:a.outerWidth*u}}),r.effect(t)},e.effects.effect.scale=function(t,n){var r=e(this),i=e.extend(!0,{},t),s=e.effects.setMode(r,t.mode||"effect"),o=parseInt(t.percent,10)||(parseInt(t.percent,10)===0?0:s==="hide"?0:100),u=t.direction||"both",a=t.origin,f={height:r.height(),width:r.width(),outerHeight:r.outerHeight(),outerWidth:r.outerWidth()},l={y:u!=="horizontal"?o/100:1,x:u!=="vertical"?o/100:1};i.effect="size",i.queue=!1,i.complete=n,s!=="effect"&&(i.origin=a||["middle","center"],i.restore=!0),i.from=t.from||(s==="show"?{height:0,width:0,outerHeight:0,outerWidth:0}:f),i.to={height:f.height*l.y,width:f.width*l.x,outerHeight:f.outerHeight*l.y,outerWidth:f.outerWidth*l.x},i.fade&&(s==="show"&&(i.from.opacity=0,i.to.opacity=1),s==="hide"&&(i.from.opacity=1,i.to.opacity=0)),r.effect(i)},e.effects.effect.size=function(t,n){var r,i,s,o=e(this),u=["position","top","bottom","left","right","width","height","overflow","opacity"],a=["position","top","bottom","left","right","overflow","opacity"],f=["width","height","overflow"],l=["fontSize"],c=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],h=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],p=e.effects.setMode(o,t.mode||"effect"),d=t.restore||p!=="effect",v=t.scale||"both",m=t.origin||["middle","center"],g=o.css("position"),y=d?u:a,b={height:0,width:0,outerHeight:0,outerWidth:0};p==="show"&&o.show(),r={height:o.height(),width:o.width(),outerHeight:o.outerHeight(),outerWidth:o.outerWidth()},t.mode==="toggle"&&p==="show"?(o.from=t.to||b,o.to=t.from||r):(o.from=t.from||(p==="show"?b:r),o.to=t.to||(p==="hide"?b:r)),s={from:{y:o.from.height/r.height,x:o.from.width/r.width},to:{y:o.to.height/r.height,x:o.to.width/r.width}};if(v==="box"||v==="both")s.from.y!==s.to.y&&(y=y.concat(c),o.from=e.effects.setTransition(o,c,s.from.y,o.from),o.to=e.effects.setTransition(o,c,s.to.y,o.to)),s.from.x!==s.to.x&&(y=y.concat(h),o.from=e.effects.setTransition(o,h,s.from.x,o.from),o.to=e.effects.setTransition(o,h,s.to.x,o.to));(v==="content"||v==="both")&&s.from.y!==s.to.y&&(y=y.concat(l).concat(f),o.from=e.effects.setTransition(o,l,s.from.y,o.from),o.to=e.effects.setTransition(o,l,s.to.y,o.to)),e.effects.save(o,y),o.show(),e.effects.createWrapper(o),o.css("overflow","hidden").css(o.from),m&&(i=e.effects.getBaseline(m,r),o.from.top=(r.outerHeight-o.outerHeight())*i.y,o.from.left=(r.outerWidth-o.outerWidth())*i.x,o.to.top=(r.outerHeight-o.to.outerHeight)*i.y,o.to.left=(r.outerWidth-o.to.outerWidth)*i.x),o.css(o.from);if(v==="content"||v==="both")c=c.concat(["marginTop","marginBottom"]).concat(l),h=h.concat(["marginLeft","marginRight"]),f=u.concat(c).concat(h),o.find("*[width]").each(function(){var n=e(this),r={height:n.height(),width:n.width(),outerHeight:n.outerHeight(),outerWidth:n.outerWidth()};d&&e.effects.save(n,f),n.from={height:r.height*s.from.y,width:r.width*s.from.x,outerHeight:r.outerHeight*s.from.y,outerWidth:r.outerWidth*s.from.x},n.to={height:r.height*s.to.y,width:r.width*s.to.x,outerHeight:r.height*s.to.y,outerWidth:r.width*s.to.x},s.from.y!==s.to.y&&(n.from=e.effects.setTransition(n,c,s.from.y,n.from),n.to=e.effects.setTransition(n,c,s.to.y,n.to)),s.from.x!==s.to.x&&(n.from=e.effects.setTransition(n,h,s.from.x,n.from),n.to=e.effects.setTransition(n,h,s.to.x,n.to)),n.css(n.from),n.animate(n.to,t.duration,t.easing,function(){d&&e.effects.restore(n,f)})});o.animate(o.to,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){o.to.opacity===0&&o.css("opacity",o.from.opacity),p==="hide"&&o.hide(),e.effects.restore(o,y),d||(g==="static"?o.css({position:"relative",top:o.to.top,left:o.to.left}):e.each(["top","left"],function(e,t){o.css(t,function(t,n){var r=parseInt(n,10),i=e?o.to.left:o.to.top;return n==="auto"?i+"px":r+i+"px"})})),e.effects.removeWrapper(o),n()}})}})(jQuery);(function(e,t){e.effects.effect.shake=function(t,n){var r=e(this),i=["position","top","bottom","left","right","height","width"],s=e.effects.setMode(r,t.mode||"effect"),o=t.direction||"left",u=t.distance||20,a=t.times||3,f=a*2+1,l=Math.round(t.duration/f),c=o==="up"||o==="down"?"top":"left",h=o==="up"||o==="left",p={},d={},v={},m,g=r.queue(),y=g.length;e.effects.save(r,i),r.show(),e.effects.createWrapper(r),p[c]=(h?"-=":"+=")+u,d[c]=(h?"+=":"-=")+u*2,v[c]=(h?"-=":"+=")+u*2,r.animate(p,l,t.easing);for(m=1;m1&&g.splice.apply(g,[1,0].concat(g.splice(y,f+1))),r.dequeue()}})(jQuery);(function(e,t){e.effects.effect.slide=function(t,n){var r=e(this),i=["position","top","bottom","left","right","width","height"],s=e.effects.setMode(r,t.mode||"show"),o=s==="show",u=t.direction||"left",a=u==="up"||u==="down"?"top":"left",f=u==="up"||u==="left",l,c={};e.effects.save(r,i),r.show(),l=t.distance||r[a==="top"?"outerHeight":"outerWidth"](!0),e.effects.createWrapper(r).css({overflow:"hidden"}),o&&r.css(a,f?isNaN(l)?"-"+l:-l:l),c[a]=(o?f?"+=":"-=":f?"-=":"+=")+l,r.animate(c,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){s==="hide"&&r.hide(),e.effects.restore(r,i),e.effects.removeWrapper(r),n()}})}})(jQuery);(function(e,t){e.effects.effect.transfer=function(t,n){var r=e(this),i=e(t.to),s=i.css("position")==="fixed",o=e("body"),u=s?o.scrollTop():0,a=s?o.scrollLeft():0,f=i.offset(),l={top:f.top-u,left:f.left-a,height:i.innerHeight(),width:i.innerWidth()},c=r.offset(),h=e('
    ').appendTo(document.body).addClass(t.className).css({top:c.top-u,left:c.left-a,height:r.innerHeight(),width:r.innerWidth(),position:s?"fixed":"absolute"}).animate(l,t.duration,t.easing,function(){h.remove(),n()})}})(jQuery);(function(e,t){var n=!1;e.widget("ui.menu",{version:"1.9.2",defaultElement:"
      ",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content ui-corner-all").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}).bind("click"+this.eventNamespace,e.proxy(function(e){this.options.disabled&&e.preventDefault()},this)),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item > a":function(e){e.preventDefault()},"click .ui-state-disabled > a":function(e){e.preventDefault()},"click .ui-menu-item:has(a)":function(t){var r=e(t.target).closest(".ui-menu-item");!n&&r.not(".ui-state-disabled").length&&(n=!0,this.select(t),r.has(".ui-menu").length?this.expand(t):this.element.is(":focus")||(this.element.trigger("focus",[!0]),this.active&&this.active.parents(".ui-menu").length===1&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(t){var n=e(t.currentTarget);n.siblings().children(".ui-state-active").removeClass("ui-state-active"),this.focus(t,n)},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(e,t){var n=this.active||this.element.children(".ui-menu-item").eq(0);t||this.focus(e,n)},blur:function(t){this._delay(function(){e.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(t){e(t.target).closest(".ui-menu").length||this.collapseAll(t),n=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").andSelf().removeClass("ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").children("a").removeUniqueId().removeClass("ui-corner-all ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var t=e(this);t.data("ui-menu-submenu-carat")&&t.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(t){function a(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var n,r,i,s,o,u=!0;switch(t.keyCode){case e.ui.keyCode.PAGE_UP:this.previousPage(t);break;case e.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case e.ui.keyCode.HOME:this._move("first","first",t);break;case e.ui.keyCode.END:this._move("last","last",t);break;case e.ui.keyCode.UP:this.previous(t);break;case e.ui.keyCode.DOWN:this.next(t);break;case e.ui.keyCode.LEFT:this.collapse(t);break;case e.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case e.ui.keyCode.ENTER:case e.ui.keyCode.SPACE:this._activate(t);break;case e.ui.keyCode.ESCAPE:this.collapse(t);break;default:u=!1,r=this.previousFilter||"",i=String.fromCharCode(t.keyCode),s=!1,clearTimeout(this.filterTimer),i===r?s=!0:i=r+i,o=new RegExp("^"+a(i),"i"),n=this.activeMenu.children(".ui-menu-item").filter(function(){return o.test(e(this).children("a").text())}),n=s&&n.index(this.active.next())!==-1?this.active.nextAll(".ui-menu-item"):n,n.length||(i=String.fromCharCode(t.keyCode),o=new RegExp("^"+a(i),"i"),n=this.activeMenu.children(".ui-menu-item").filter(function(){return o.test(e(this).children("a").text())})),n.length?(this.focus(t,n),n.length>1?(this.previousFilter=i,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter):delete this.previousFilter}u&&t.preventDefault()},_activate:function(e){this.active.is(".ui-state-disabled")||(this.active.children("a[aria-haspopup='true']").length?this.expand(e):this.select(e))},refresh:function(){var t,n=this.options.icons.submenu,r=this.element.find(this.options.menus);r.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-corner-all").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=e(this),r=t.prev("a"),i=e("").addClass("ui-menu-icon ui-icon "+n).data("ui-menu-submenu-carat",!0);r.attr("aria-haspopup","true").prepend(i),t.attr("aria-labelledby",r.attr("id"))}),t=r.add(this.element),t.children(":not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","presentation").children("a").uniqueId().addClass("ui-corner-all").attr({tabIndex:-1,role:this._itemRole()}),t.children(":not(.ui-menu-item)").each(function(){var t=e(this);/[^\-—–\s]/.test(t.text())||t.addClass("ui-widget-content ui-menu-divider")}),t.children(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!e.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},focus:function(e,t){var n,r;this.blur(e,e&&e.type==="focus"),this._scrollIntoView(t),this.active=t.first(),r=this.active.children("a").addClass("ui-state-focus"),this.options.role&&this.element.attr("aria-activedescendant",r.attr("id")),this.active.parent().closest(".ui-menu-item").children("a:first").addClass("ui-state-active"),e&&e.type==="keydown"?this._close():this.timer=this._delay(function(){this._close()},this.delay),n=t.children(".ui-menu"),n.length&&/^mouse/.test(e.type)&&this._startOpening(n),this.activeMenu=t.parent(),this._trigger("focus",e,{item:t})},_scrollIntoView:function(t){var n,r,i,s,o,u;this._hasScroll()&&(n=parseFloat(e.css(this.activeMenu[0],"borderTopWidth"))||0,r=parseFloat(e.css(this.activeMenu[0],"paddingTop"))||0,i=t.offset().top-this.activeMenu.offset().top-n-r,s=this.activeMenu.scrollTop(),o=this.activeMenu.height(),u=t.height(),i<0?this.activeMenu.scrollTop(s+i):i+u>o&&this.activeMenu.scrollTop(s+i-o+u))},blur:function(e,t){t||clearTimeout(this.timer);if(!this.active)return;this.active.children("a").removeClass("ui-state-focus"),this.active=null,this._trigger("blur",e,{item:this.active})},_startOpening:function(e){clearTimeout(this.timer);if(e.attr("aria-hidden")!=="true")return;this.timer=this._delay(function(){this._close(),this._open(e)},this.delay)},_open:function(t){var n=e.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(t.parents(".ui-menu")).hide().attr("aria-hidden","true"),t.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(n)},collapseAll:function(t,n){clearTimeout(this.timer),this.timer=this._delay(function(){var r=n?this.element:e(t&&t.target).closest(this.element.find(".ui-menu"));r.length||(r=this.element),this._close(r),this.blur(t),this.activeMenu=r},this.delay)},_close:function(e){e||(e=this.active?this.active.parent():this.element),e.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find("a.ui-state-active").removeClass("ui-state-active")},collapse:function(e){var t=this.active&&this.active.parent().closest(".ui-menu-item",this.element);t&&t.length&&(this._close(),this.focus(e,t))},expand:function(e){var t=this.active&&this.active.children(".ui-menu ").children(".ui-menu-item").first();t&&t.length&&(this._open(t.parent()),this._delay(function(){this.focus(e,t)}))},next:function(e){this._move("next","first",e)},previous:function(e){this._move("prev","last",e)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(e,t,n){var r;this.active&&(e==="first"||e==="last"?r=this.active[e==="first"?"prevAll":"nextAll"](".ui-menu-item").eq(-1):r=this.active[e+"All"](".ui-menu-item").eq(0));if(!r||!r.length||!this.active)r=this.activeMenu.children(".ui-menu-item")[t]();this.focus(n,r)},nextPage:function(t){var n,r,i;if(!this.active){this.next(t);return}if(this.isLastItem())return;this._hasScroll()?(r=this.active.offset().top,i=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return n=e(this),n.offset().top-r-i<0}),this.focus(t,n)):this.focus(t,this.activeMenu.children(".ui-menu-item")[this.active?"last":"first"]())},previousPage:function(t){var n,r,i;if(!this.active){this.next(t);return}if(this.isFirstItem())return;this._hasScroll()?(r=this.active.offset().top,i=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return n=e(this),n.offset().top-r+i>0}),this.focus(t,n)):this.focus(t,this.activeMenu.children(".ui-menu-item").first())},_hasScroll:function(){return this.element.outerHeight()
    ").appendTo(this.element),this.oldValue=this._value(),this._refreshValue()},_destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow"),this.valueDiv.remove()},value:function(e){return e===t?this._value():(this._setOption("value",e),this)},_setOption:function(e,t){e==="value"&&(this.options.value=t,this._refreshValue(),this._value()===this.options.max&&this._trigger("complete")),this._super(e,t)},_value:function(){var e=this.options.value;return typeof e!="number"&&(e=0),Math.min(this.options.max,Math.max(this.min,e))},_percentage:function(){return 100*this._value()/this.options.max},_refreshValue:function(){var e=this.value(),t=this._percentage();this.oldValue!==e&&(this.oldValue=e,this._trigger("change")),this.valueDiv.toggle(e>this.min).toggleClass("ui-corner-right",e===this.options.max).width(t.toFixed(0)+"%"),this.element.attr("aria-valuenow",e)}})})(jQuery);(function(e,t){e.widget("ui.resizable",e.ui.mouse,{version:"1.9.2",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1e3},_create:function(){var t=this,n=this.options;this.element.addClass("ui-resizable"),e.extend(this,{_aspectRatio:!!n.aspectRatio,aspectRatio:n.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:n.helper||n.ghost||n.animate?n.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(e('
    ').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("resizable",this.element.data("resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=n.handles||(e(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se");if(this.handles.constructor==String){this.handles=="all"&&(this.handles="n,e,s,w,se,sw,ne,nw");var r=this.handles.split(",");this.handles={};for(var i=0;i');u.css({zIndex:n.zIndex}),"se"==s&&u.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(u)}}this._renderAxis=function(t){t=t||this.element;for(var n in this.handles){this.handles[n].constructor==String&&(this.handles[n]=e(this.handles[n],this.element).show());if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var r=e(this.handles[n],this.element),i=0;i=/sw|ne|nw|se|n|s/.test(n)?r.outerHeight():r.outerWidth();var s=["padding",/ne|nw|n/.test(n)?"Top":/se|sw|s/.test(n)?"Bottom":/^e$/.test(n)?"Right":"Left"].join("");t.css(s,i),this._proportionallyResize()}if(!e(this.handles[n]).length)continue}},this._renderAxis(this.element),this._handles=e(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){if(!t.resizing){if(this.className)var e=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);t.axis=e&&e[1]?e[1]:"se"}}),n.autoHide&&(this._handles.hide(),e(this.element).addClass("ui-resizable-autohide").mouseenter(function(){if(n.disabled)return;e(this).removeClass("ui-resizable-autohide"),t._handles.show()}).mouseleave(function(){if(n.disabled)return;t.resizing||(e(this).addClass("ui-resizable-autohide"),t._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t=function(t){e(t).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){t(this.element);var n=this.element;this.originalElement.css({position:n.css("position"),width:n.outerWidth(),height:n.outerHeight(),top:n.css("top"),left:n.css("left")}).insertAfter(n),n.remove()}return this.originalElement.css("resize",this.originalResizeStyle),t(this.originalElement),this},_mouseCapture:function(t){var n=!1;for(var r in this.handles)e(this.handles[r])[0]==t.target&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(t){var r=this.options,i=this.element.position(),s=this.element;this.resizing=!0,this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()},(s.is(".ui-draggable")||/absolute/.test(s.css("position")))&&s.css({position:"absolute",top:i.top,left:i.left}),this._renderProxy();var o=n(this.helper.css("left")),u=n(this.helper.css("top"));r.containment&&(o+=e(r.containment).scrollLeft()||0,u+=e(r.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:o,top:u},this.size=this._helper?{width:s.outerWidth(),height:s.outerHeight()}:{width:s.width(),height:s.height()},this.originalSize=this._helper?{width:s.outerWidth(),height:s.outerHeight()}:{width:s.width(),height:s.height()},this.originalPosition={left:o,top:u},this.sizeDiff={width:s.outerWidth()-s.width(),height:s.outerHeight()-s.height()},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio=typeof r.aspectRatio=="number"?r.aspectRatio:this.originalSize.width/this.originalSize.height||1;var a=e(".ui-resizable-"+this.axis).css("cursor");return e("body").css("cursor",a=="auto"?this.axis+"-resize":a),s.addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(e){var t=this.helper,n=this.options,r={},i=this,s=this.originalMousePosition,o=this.axis,u=e.pageX-s.left||0,a=e.pageY-s.top||0,f=this._change[o];if(!f)return!1;var l=f.apply(this,[e,u,a]);this._updateVirtualBoundaries(e.shiftKey);if(this._aspectRatio||e.shiftKey)l=this._updateRatio(l,e);return l=this._respectSize(l,e),this._propagate("resize",e),t.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"}),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),this._updateCache(l),this._trigger("resize",e,this.ui()),!1},_mouseStop:function(t){this.resizing=!1;var n=this.options,r=this;if(this._helper){var i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),o=s&&e.ui.hasScroll(i[0],"left")?0:r.sizeDiff.height,u=s?0:r.sizeDiff.width,a={width:r.helper.width()-u,height:r.helper.height()-o},f=parseInt(r.element.css("left"),10)+(r.position.left-r.originalPosition.left)||null,l=parseInt(r.element.css("top"),10)+(r.position.top-r.originalPosition.top)||null;n.animate||this.element.css(e.extend(a,{top:l,left:f})),r.helper.height(r.size.height),r.helper.width(r.size.width),this._helper&&!n.animate&&this._proportionallyResize()}return e("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(e){var t=this.options,n,i,s,o,u;u={minWidth:r(t.minWidth)?t.minWidth:0,maxWidth:r(t.maxWidth)?t.maxWidth:Infinity,minHeight:r(t.minHeight)?t.minHeight:0,maxHeight:r(t.maxHeight)?t.maxHeight:Infinity};if(this._aspectRatio||e)n=u.minHeight*this.aspectRatio,s=u.minWidth/this.aspectRatio,i=u.maxHeight*this.aspectRatio,o=u.maxWidth/this.aspectRatio,n>u.minWidth&&(u.minWidth=n),s>u.minHeight&&(u.minHeight=s),ie.width,l=r(e.height)&&i.minHeight&&i.minHeight>e.height;f&&(e.width=i.minWidth),l&&(e.height=i.minHeight),u&&(e.width=i.maxWidth),a&&(e.height=i.maxHeight);var c=this.originalPosition.left+this.originalSize.width,h=this.position.top+this.size.height,p=/sw|nw|w/.test(o),d=/nw|ne|n/.test(o);f&&p&&(e.left=c-i.minWidth),u&&p&&(e.left=c-i.maxWidth),l&&d&&(e.top=h-i.minHeight),a&&d&&(e.top=h-i.maxHeight);var v=!e.width&&!e.height;return v&&!e.left&&e.top?e.top=null:v&&!e.top&&e.left&&(e.left=null),e},_proportionallyResize:function(){var t=this.options;if(!this._proportionallyResizeElements.length)return;var n=this.helper||this.element;for(var r=0;r');var r=e.ui.ie6?1:0,i=e.ui.ie6?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+i,height:this.element.outerHeight()+i,position:"absolute",left:this.elementOffset.left-r+"px",top:this.elementOffset.top-r+"px",zIndex:++n.zIndex}),this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(e,t,n){return{width:this.originalSize.width+t}},w:function(e,t,n){var r=this.options,i=this.originalSize,s=this.originalPosition;return{left:s.left+t,width:i.width-t}},n:function(e,t,n){var r=this.options,i=this.originalSize,s=this.originalPosition;return{top:s.top+n,height:i.height-n}},s:function(e,t,n){return{height:this.originalSize.height+n}},se:function(t,n,r){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,n,r]))},sw:function(t,n,r){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,n,r]))},ne:function(t,n,r){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,n,r]))},nw:function(t,n,r){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,n,r]))}},_propagate:function(t,n){e.ui.plugin.call(this,t,[n,this.ui()]),t!="resize"&&this._trigger(t,n,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),e.ui.plugin.add("resizable","alsoResize",{start:function(t,n){var r=e(this).data("resizable"),i=r.options,s=function(t){e(t).each(function(){var t=e(this);t.data("resizable-alsoresize",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)})})};typeof i.alsoResize=="object"&&!i.alsoResize.parentNode?i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):e.each(i.alsoResize,function(e){s(e)}):s(i.alsoResize)},resize:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r.originalSize,o=r.originalPosition,u={height:r.size.height-s.height||0,width:r.size.width-s.width||0,top:r.position.top-o.top||0,left:r.position.left-o.left||0},a=function(t,r){e(t).each(function(){var t=e(this),i=e(this).data("resizable-alsoresize"),s={},o=r&&r.length?r:t.parents(n.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(o,function(e,t){var n=(i[t]||0)+(u[t]||0);n&&n>=0&&(s[t]=n||null)}),t.css(s)})};typeof i.alsoResize=="object"&&!i.alsoResize.nodeType?e.each(i.alsoResize,function(e,t){a(e,t)}):a(i.alsoResize)},stop:function(t,n){e(this).removeData("resizable-alsoresize")}}),e.ui.plugin.add("resizable","animate",{stop:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r._proportionallyResizeElements,o=s.length&&/textarea/i.test(s[0].nodeName),u=o&&e.ui.hasScroll(s[0],"left")?0:r.sizeDiff.height,a=o?0:r.sizeDiff.width,f={width:r.size.width-a,height:r.size.height-u},l=parseInt(r.element.css("left"),10)+(r.position.left-r.originalPosition.left)||null,c=parseInt(r.element.css("top"),10)+(r.position.top-r.originalPosition.top)||null;r.element.animate(e.extend(f,c&&l?{top:c,left:l}:{}),{duration:i.animateDuration,easing:i.animateEasing,step:function(){var n={width:parseInt(r.element.css("width"),10),height:parseInt(r.element.css("height"),10),top:parseInt(r.element.css("top"),10),left:parseInt(r.element.css("left"),10)};s&&s.length&&e(s[0]).css({width:n.width,height:n.height}),r._updateCache(n),r._propagate("resize",t)}})}}),e.ui.plugin.add("resizable","containment",{start:function(t,r){var i=e(this).data("resizable"),s=i.options,o=i.element,u=s.containment,a=u instanceof e?u.get(0):/parent/.test(u)?o.parent().get(0):u;if(!a)return;i.containerElement=e(a);if(/document/.test(u)||u==document)i.containerOffset={left:0,top:0},i.containerPosition={left:0,top:0},i.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight};else{var f=e(a),l=[];e(["Top","Right","Left","Bottom"]).each(function(e,t){l[e]=n(f.css("padding"+t))}),i.containerOffset=f.offset(),i.containerPosition=f.position(),i.containerSize={height:f.innerHeight()-l[3],width:f.innerWidth()-l[1]};var c=i.containerOffset,h=i.containerSize.height,p=i.containerSize.width,d=e.ui.hasScroll(a,"left")?a.scrollWidth:p,v=e.ui.hasScroll(a)?a.scrollHeight:h;i.parentData={element:a,left:c.left,top:c.top,width:d,height:v}}},resize:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r.containerSize,o=r.containerOffset,u=r.size,a=r.position,f=r._aspectRatio||t.shiftKey,l={top:0,left:0},c=r.containerElement;c[0]!=document&&/static/.test(c.css("position"))&&(l=o),a.left<(r._helper?o.left:0)&&(r.size.width=r.size.width+(r._helper?r.position.left-o.left:r.position.left-l.left),f&&(r.size.height=r.size.width/r.aspectRatio),r.position.left=i.helper?o.left:0),a.top<(r._helper?o.top:0)&&(r.size.height=r.size.height+(r._helper?r.position.top-o.top:r.position.top),f&&(r.size.width=r.size.height*r.aspectRatio),r.position.top=r._helper?o.top:0),r.offset.left=r.parentData.left+r.position.left,r.offset.top=r.parentData.top+r.position.top;var h=Math.abs((r._helper?r.offset.left-l.left:r.offset.left-l.left)+r.sizeDiff.width),p=Math.abs((r._helper?r.offset.top-l.top:r.offset.top-o.top)+r.sizeDiff.height),d=r.containerElement.get(0)==r.element.parent().get(0),v=/relative|absolute/.test(r.containerElement.css("position"));d&&v&&(h-=r.parentData.left),h+r.size.width>=r.parentData.width&&(r.size.width=r.parentData.width-h,f&&(r.size.height=r.size.width/r.aspectRatio)),p+r.size.height>=r.parentData.height&&(r.size.height=r.parentData.height-p,f&&(r.size.width=r.size.height*r.aspectRatio))},stop:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r.position,o=r.containerOffset,u=r.containerPosition,a=r.containerElement,f=e(r.helper),l=f.offset(),c=f.outerWidth()-r.sizeDiff.width,h=f.outerHeight()-r.sizeDiff.height;r._helper&&!i.animate&&/relative/.test(a.css("position"))&&e(this).css({left:l.left-u.left-o.left,width:c,height:h}),r._helper&&!i.animate&&/static/.test(a.css("position"))&&e(this).css({left:l.left-u.left-o.left,width:c,height:h})}}),e.ui.plugin.add("resizable","ghost",{start:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r.size;r.ghost=r.originalElement.clone(),r.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof i.ghost=="string"?i.ghost:""),r.ghost.appendTo(r.helper)},resize:function(t,n){var r=e(this).data("resizable"),i=r.options;r.ghost&&r.ghost.css({position:"relative",height:r.size.height,width:r.size.width})},stop:function(t,n){var r=e(this).data("resizable"),i=r.options;r.ghost&&r.helper&&r.helper.get(0).removeChild(r.ghost.get(0))}}),e.ui.plugin.add("resizable","grid",{resize:function(t,n){var r=e(this).data("resizable"),i=r.options,s=r.size,o=r.originalSize,u=r.originalPosition,a=r.axis,f=i._aspectRatio||t.shiftKey;i.grid=typeof i.grid=="number"?[i.grid,i.grid]:i.grid;var l=Math.round((s.width-o.width)/(i.grid[0]||1))*(i.grid[0]||1),c=Math.round((s.height-o.height)/(i.grid[1]||1))*(i.grid[1]||1);/^(se|s|e)$/.test(a)?(r.size.width=o.width+l,r.size.height=o.height+c):/^(ne)$/.test(a)?(r.size.width=o.width+l,r.size.height=o.height+c,r.position.top=u.top-c):/^(sw)$/.test(a)?(r.size.width=o.width+l,r.size.height=o.height+c,r.position.left=u.left-l):(r.size.width=o.width+l,r.size.height=o.height+c,r.position.top=u.top-c,r.position.left=u.left-l)}});var n=function(e){return parseInt(e,10)||0},r=function(e){return!isNaN(parseInt(e,10))}})(jQuery);(function(e,t){e.widget("ui.selectable",e.ui.mouse,{version:"1.9.2",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch"},_create:function(){var t=this;this.element.addClass("ui-selectable"),this.dragged=!1;var n;this.refresh=function(){n=e(t.options.filter,t.element[0]),n.addClass("ui-selectee"),n.each(function(){var t=e(this),n=t.offset();e.data(this,"selectable-item",{element:this,$element:t,left:n.left,top:n.top,right:n.left+t.outerWidth(),bottom:n.top+t.outerHeight(),startselected:!1,selected:t.hasClass("ui-selected"),selecting:t.hasClass("ui-selecting"),unselecting:t.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=n.addClass("ui-selectee"),this._mouseInit(),this.helper=e("
    ")},_destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled"),this._mouseDestroy()},_mouseStart:function(t){var n=this;this.opos=[t.pageX,t.pageY];if(this.options.disabled)return;var r=this.options;this.selectees=e(r.filter,this.element[0]),this._trigger("start",t),e(r.appendTo).append(this.helper),this.helper.css({left:t.clientX,top:t.clientY,width:0,height:0}),r.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var r=e.data(this,"selectable-item");r.startselected=!0,!t.metaKey&&!t.ctrlKey&&(r.$element.removeClass("ui-selected"),r.selected=!1,r.$element.addClass("ui-unselecting"),r.unselecting=!0,n._trigger("unselecting",t,{unselecting:r.element}))}),e(t.target).parents().andSelf().each(function(){var r=e.data(this,"selectable-item");if(r){var i=!t.metaKey&&!t.ctrlKey||!r.$element.hasClass("ui-selected");return r.$element.removeClass(i?"ui-unselecting":"ui-selected").addClass(i?"ui-selecting":"ui-unselecting"),r.unselecting=!i,r.selecting=i,r.selected=i,i?n._trigger("selecting",t,{selecting:r.element}):n._trigger("unselecting",t,{unselecting:r.element}),!1}})},_mouseDrag:function(t){var n=this;this.dragged=!0;if(this.options.disabled)return;var r=this.options,i=this.opos[0],s=this.opos[1],o=t.pageX,u=t.pageY;if(i>o){var a=o;o=i,i=a}if(s>u){var a=u;u=s,s=a}return this.helper.css({left:i,top:s,width:o-i,height:u-s}),this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!a||a.element==n.element[0])return;var f=!1;r.tolerance=="touch"?f=!(a.left>o||a.rightu||a.bottomi&&a.rights&&a.bottom").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(i.range==="min"||i.range==="max"?" ui-slider-range-"+i.range:""))),r=i.values&&i.values.length||1;for(t=s.length;tn&&(i=n,s=e(this),o=t)}),c.range===!0&&this.values(1)===c.min&&(o+=1,s=e(this.handles[o])),u=this._start(t,o),u===!1?!1:(this._mouseSliding=!0,this._handleIndex=o,s.addClass("ui-state-active").focus(),a=s.offset(),f=!e(t.target).parents().andSelf().is(".ui-slider-handle"),this._clickOffset=f?{left:0,top:0}:{left:t.pageX-a.left-s.width()/2,top:t.pageY-a.top-s.height()/2-(parseInt(s.css("borderTopWidth"),10)||0)-(parseInt(s.css("borderBottomWidth"),10)||0)+(parseInt(s.css("marginTop"),10)||0)},this.handles.hasClass("ui-state-hover")||this._slide(t,o,r),this._animateOff=!0,!0))},_mouseStart:function(){return!0},_mouseDrag:function(e){var t={x:e.pageX,y:e.pageY},n=this._normValueFromMouse(t);return this._slide(e,this._handleIndex,n),!1},_mouseStop:function(e){return this.handles.removeClass("ui-state-active"),this._mouseSliding=!1,this._stop(e,this._handleIndex),this._change(e,this._handleIndex),this._handleIndex=null,this._clickOffset=null,this._animateOff=!1,!1},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(e){var t,n,r,i,s;return this.orientation==="horizontal"?(t=this.elementSize.width,n=e.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)):(t=this.elementSize.height,n=e.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)),r=n/t,r>1&&(r=1),r<0&&(r=0),this.orientation==="vertical"&&(r=1-r),i=this._valueMax()-this._valueMin(),s=this._valueMin()+r*i,this._trimAlignValue(s)},_start:function(e,t){var n={handle:this.handles[t],value:this.value()};return this.options.values&&this.options.values.length&&(n.value=this.values(t),n.values=this.values()),this._trigger("start",e,n)},_slide:function(e,t,n){var r,i,s;this.options.values&&this.options.values.length?(r=this.values(t?0:1),this.options.values.length===2&&this.options.range===!0&&(t===0&&n>r||t===1&&n1){this.options.values[t]=this._trimAlignValue(n),this._refreshValue(),this._change(null,t);return}if(!arguments.length)return this._values();if(!e.isArray(arguments[0]))return this.options.values&&this.options.values.length?this._values(t):this.value();r=this.options.values,i=arguments[0];for(s=0;s=this._valueMax())return this._valueMax();var t=this.options.step>0?this.options.step:1,n=(e-this._valueMin())%t,r=e-n;return Math.abs(n)*2>=t&&(r+=n>0?t:-t),parseFloat(r.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},_refreshValue:function(){var t,n,r,i,s,o=this.options.range,u=this.options,a=this,f=this._animateOff?!1:u.animate,l={};this.options.values&&this.options.values.length?this.handles.each(function(r){n=(a.values(r)-a._valueMin())/(a._valueMax()-a._valueMin())*100,l[a.orientation==="horizontal"?"left":"bottom"]=n+"%",e(this).stop(1,1)[f?"animate":"css"](l,u.animate),a.options.range===!0&&(a.orientation==="horizontal"?(r===0&&a.range.stop(1,1)[f?"animate":"css"]({left:n+"%"},u.animate),r===1&&a.range[f?"animate":"css"]({width:n-t+"%"},{queue:!1,duration:u.animate})):(r===0&&a.range.stop(1,1)[f?"animate":"css"]({bottom:n+"%"},u.animate),r===1&&a.range[f?"animate":"css"]({height:n-t+"%"},{queue:!1,duration:u.animate}))),t=n}):(r=this.value(),i=this._valueMin(),s=this._valueMax(),n=s!==i?(r-i)/(s-i)*100:0,l[this.orientation==="horizontal"?"left":"bottom"]=n+"%",this.handle.stop(1,1)[f?"animate":"css"](l,u.animate),o==="min"&&this.orientation==="horizontal"&&this.range.stop(1,1)[f?"animate":"css"]({width:n+"%"},u.animate),o==="max"&&this.orientation==="horizontal"&&this.range[f?"animate":"css"]({width:100-n+"%"},{queue:!1,duration:u.animate}),o==="min"&&this.orientation==="vertical"&&this.range.stop(1,1)[f?"animate":"css"]({height:n+"%"},u.animate),o==="max"&&this.orientation==="vertical"&&this.range[f?"animate":"css"]({height:100-n+"%"},{queue:!1,duration:u.animate}))}})})(jQuery);(function(e,t){e.widget("ui.sortable",e.ui.mouse,{version:"1.9.2",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3},_create:function(){var e=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?e.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var e=this.items.length-1;e>=0;e--)this.items[e].item.removeData(this.widgetName+"-item");return this},_setOption:function(t,n){t==="disabled"?(this.options[t]=n,this.widget().toggleClass("ui-sortable-disabled",!!n)):e.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(t,n){var r=this;if(this.reverting)return!1;if(this.options.disabled||this.options.type=="static")return!1;this._refreshItems(t);var i=null,s=e(t.target).parents().each(function(){if(e.data(this,r.widgetName+"-item")==r)return i=e(this),!1});e.data(t.target,r.widgetName+"-item")==r&&(i=e(t.target));if(!i)return!1;if(this.options.handle&&!n){var o=!1;e(this.options.handle,i).find("*").andSelf().each(function(){this==t.target&&(o=!0)});if(!o)return!1}return this.currentItem=i,this._removeCurrentsFromItems(),!0},_mouseStart:function(t,n,r){var i=this.options;this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(t),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!=this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),i.containment&&this._setContainment(),i.cursor&&(e("body").css("cursor")&&(this._storedCursor=e("body").css("cursor")),e("body").css("cursor",i.cursor)),i.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",i.opacity)),i.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",i.zIndex)),this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",t,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions();if(!r)for(var s=this.containers.length-1;s>=0;s--)this.containers[s]._trigger("activate",t,this._uiHash(this));return e.ui.ddmanager&&(e.ui.ddmanager.current=this),e.ui.ddmanager&&!i.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(t),!0},_mouseDrag:function(t){this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs);if(this.options.scroll){var n=this.options,r=!1;this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-t.pageY=0;i--){var s=this.items[i],o=s.item[0],u=this._intersectsWithPointer(s);if(!u)continue;if(s.instance!==this.currentContainer)continue;if(o!=this.currentItem[0]&&this.placeholder[u==1?"next":"prev"]()[0]!=o&&!e.contains(this.placeholder[0],o)&&(this.options.type=="semi-dynamic"?!e.contains(this.element[0],o):!0)){this.direction=u==1?"down":"up";if(this.options.tolerance!="pointer"&&!this._intersectsWithSides(s))break;this._rearrange(t,s),this._trigger("change",t,this._uiHash());break}}return this._contactContainers(t),e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),this._trigger("sort",t,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(t,n){if(!t)return;e.ui.ddmanager&&!this.options.dropBehaviour&&e.ui.ddmanager.drop(this,t);if(this.options.revert){var r=this,i=this.placeholder.offset();this.reverting=!0,e(this.helper).animate({left:i.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:i.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){r._clear(t)})}else this._clear(t,n);return!1},cancel:function(){if(this.dragging){this._mouseUp({target:null}),this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var t=this.containers.length-1;t>=0;t--)this.containers[t]._trigger("deactivate",null,this._uiHash(this)),this.containers[t].containerCache.over&&(this.containers[t]._trigger("out",null,this._uiHash(this)),this.containers[t].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),e.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?e(this.domPosition.prev).after(this.currentItem):e(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(t){var n=this._getItemsAsjQuery(t&&t.connected),r=[];return t=t||{},e(n).each(function(){var n=(e(t.item||this).attr(t.attribute||"id")||"").match(t.expression||/(.+)[-=_](.+)/);n&&r.push((t.key||n[1]+"[]")+"="+(t.key&&t.expression?n[1]:n[2]))}),!r.length&&t.key&&r.push(t.key+"="),r.join("&")},toArray:function(t){var n=this._getItemsAsjQuery(t&&t.connected),r=[];return t=t||{},n.each(function(){r.push(e(t.item||this).attr(t.attribute||"id")||"")}),r},_intersectsWith:function(e){var t=this.positionAbs.left,n=t+this.helperProportions.width,r=this.positionAbs.top,i=r+this.helperProportions.height,s=e.left,o=s+e.width,u=e.top,a=u+e.height,f=this.offset.click.top,l=this.offset.click.left,c=r+f>u&&r+fs&&t+le[this.floating?"width":"height"]?c:s0?"down":"up")},_getDragHorizontalDirection:function(){var e=this.positionAbs.left-this.lastPositionAbs.left;return e!=0&&(e>0?"right":"left")},refresh:function(e){return this._refreshItems(e),this.refreshPositions(),this},_connectWith:function(){var e=this.options;return e.connectWith.constructor==String?[e.connectWith]:e.connectWith},_getItemsAsjQuery:function(t){var n=[],r=[],i=this._connectWith();if(i&&t)for(var s=i.length-1;s>=0;s--){var o=e(i[s]);for(var u=o.length-1;u>=0;u--){var a=e.data(o[u],this.widgetName);a&&a!=this&&!a.options.disabled&&r.push([e.isFunction(a.options.items)?a.options.items.call(a.element):e(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a])}}r.push([e.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):e(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]);for(var s=r.length-1;s>=0;s--)r[s][0].each(function(){n.push(this)});return e(n)},_removeCurrentsFromItems:function(){var t=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=e.grep(this.items,function(e){for(var n=0;n=0;s--){var o=e(i[s]);for(var u=o.length-1;u>=0;u--){var a=e.data(o[u],this.widgetName);a&&a!=this&&!a.options.disabled&&(r.push([e.isFunction(a.options.items)?a.options.items.call(a.element[0],t,{item:this.currentItem}):e(a.options.items,a.element),a]),this.containers.push(a))}}for(var s=r.length-1;s>=0;s--){var f=r[s][1],l=r[s][0];for(var u=0,c=l.length;u=0;n--){var r=this.items[n];if(r.instance!=this.currentContainer&&this.currentContainer&&r.item[0]!=this.currentItem[0])continue;var i=this.options.toleranceElement?e(this.options.toleranceElement,r.item):r.item;t||(r.width=i.outerWidth(),r.height=i.outerHeight());var s=i.offset();r.left=s.left,r.top=s.top}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(var n=this.containers.length-1;n>=0;n--){var s=this.containers[n].element.offset();this.containers[n].containerCache.left=s.left,this.containers[n].containerCache.top=s.top,this.containers[n].containerCache.width=this.containers[n].element.outerWidth(),this.containers[n].containerCache.height=this.containers[n].element.outerHeight()}return this},_createPlaceholder:function(t){t=t||this;var n=t.options;if(!n.placeholder||n.placeholder.constructor==String){var r=n.placeholder;n.placeholder={element:function(){var n=e(document.createElement(t.currentItem[0].nodeName)).addClass(r||t.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];return r||(n.style.visibility="hidden"),n},update:function(e,i){if(r&&!n.forcePlaceholderSize)return;i.height()||i.height(t.currentItem.innerHeight()-parseInt(t.currentItem.css("paddingTop")||0,10)-parseInt(t.currentItem.css("paddingBottom")||0,10)),i.width()||i.width(t.currentItem.innerWidth()-parseInt(t.currentItem.css("paddingLeft")||0,10)-parseInt(t.currentItem.css("paddingRight")||0,10))}}}t.placeholder=e(n.placeholder.element.call(t.element,t.currentItem)),t.currentItem.after(t.placeholder),n.placeholder.update(t,t.placeholder)},_contactContainers:function(t){var n=null,r=null;for(var i=this.containers.length-1;i>=0;i--){if(e.contains(this.currentItem[0],this.containers[i].element[0]))continue;if(this._intersectsWith(this.containers[i].containerCache)){if(n&&e.contains(this.containers[i].element[0],n.element[0]))continue;n=this.containers[i],r=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",t,this._uiHash(this)),this.containers[i].containerCache.over=0)}if(!n)return;if(this.containers.length===1)this.containers[r]._trigger("over",t,this._uiHash(this)),this.containers[r].containerCache.over=1;else{var s=1e4,o=null,u=this.containers[r].floating?"left":"top",a=this.containers[r].floating?"width":"height",f=this.positionAbs[u]+this.offset.click[u];for(var l=this.items.length-1;l>=0;l--){if(!e.contains(this.containers[r].element[0],this.items[l].item[0]))continue;if(this.items[l].item[0]==this.currentItem[0])continue;var c=this.items[l].item.offset()[u],h=!1;Math.abs(c-f)>Math.abs(c+this.items[l][a]-f)&&(h=!0,c+=this.items[l][a]),Math.abs(c-f)this.containment[2]&&(s=this.containment[2]+this.offset.click.left),t.pageY-this.offset.click.top>this.containment[3]&&(o=this.containment[3]+this.offset.click.top));if(n.grid){var u=this.originalPageY+Math.round((o-this.originalPageY)/n.grid[1])*n.grid[1];o=this.containment?u-this.offset.click.topthis.containment[3]?u-this.offset.click.topthis.containment[2]?a-this.offset.click.left=0;i--)n||r.push(function(e){return function(t){e._trigger("deactivate",t,this._uiHash(this))}}.call(this,this.containers[i])),this.containers[i].containerCache.over&&(r.push(function(e){return function(t){e._trigger("out",t,this._uiHash(this))}}.call(this,this.containers[i])),this.containers[i].containerCache.over=0);this._storedCursor&&e("body").css("cursor",this._storedCursor),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex),this.dragging=!1;if(this.cancelHelperRemoval){if(!n){this._trigger("beforeStop",t,this._uiHash());for(var i=0;i",widgetEventPrefix:"spin",options:{culture:null,icons:{down:"ui-icon-triangle-1-s",up:"ui-icon-triangle-1-n"},incremental:!0,max:null,min:null,numberFormat:null,page:10,step:1,change:null,spin:null,start:null,stop:null},_create:function(){this._setOption("max",this.options.max),this._setOption("min",this.options.min),this._setOption("step",this.options.step),this._value(this.element.val(),!0),this._draw(),this._on(this._events),this._refresh(),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_getCreateOptions:function(){var t={},n=this.element;return e.each(["min","max","step"],function(e,r){var i=n.attr(r);i!==undefined&&i.length&&(t[r]=i)}),t},_events:{keydown:function(e){this._start(e)&&this._keydown(e)&&e.preventDefault()},keyup:"_stop",focus:function(){this.previous=this.element.val()},blur:function(e){if(this.cancelBlur){delete this.cancelBlur;return}this._refresh(),this.previous!==this.element.val()&&this._trigger("change",e)},mousewheel:function(e,t){if(!t)return;if(!this.spinning&&!this._start(e))return!1;this._spin((t>0?1:-1)*this.options.step,e),clearTimeout(this.mousewheelTimer),this.mousewheelTimer=this._delay(function(){this.spinning&&this._stop(e)},100),e.preventDefault()},"mousedown .ui-spinner-button":function(t){function r(){var e=this.element[0]===this.document[0].activeElement;e||(this.element.focus(),this.previous=n,this._delay(function(){this.previous=n}))}var n;n=this.element[0]===this.document[0].activeElement?this.previous:this.element.val(),t.preventDefault(),r.call(this),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,r.call(this)});if(this._start(t)===!1)return;this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t)},"mouseup .ui-spinner-button":"_stop","mouseenter .ui-spinner-button":function(t){if(!e(t.currentTarget).hasClass("ui-state-active"))return;if(this._start(t)===!1)return!1;this._repeat(null,e(t.currentTarget).hasClass("ui-spinner-up")?1:-1,t)},"mouseleave .ui-spinner-button":"_stop"},_draw:function(){var e=this.uiSpinner=this.element.addClass("ui-spinner-input").attr("autocomplete","off").wrap(this._uiSpinnerHtml()).parent().append(this._buttonHtml());this.element.attr("role","spinbutton"),this.buttons=e.find(".ui-spinner-button").attr("tabIndex",-1).button().removeClass("ui-corner-all"),this.buttons.height()>Math.ceil(e.height()*.5)&&e.height()>0&&e.height(e.height()),this.options.disabled&&this.disable()},_keydown:function(t){var n=this.options,r=e.ui.keyCode;switch(t.keyCode){case r.UP:return this._repeat(null,1,t),!0;case r.DOWN:return this._repeat(null,-1,t),!0;case r.PAGE_UP:return this._repeat(null,n.page,t),!0;case r.PAGE_DOWN:return this._repeat(null,-n.page,t),!0}return!1},_uiSpinnerHtml:function(){return""},_buttonHtml:function(){return""+""+""+""+""},_start:function(e){return!this.spinning&&this._trigger("start",e)===!1?!1:(this.counter||(this.counter=1),this.spinning=!0,!0)},_repeat:function(e,t,n){e=e||500,clearTimeout(this.timer),this.timer=this._delay(function(){this._repeat(40,t,n)},e),this._spin(t*this.options.step,n)},_spin:function(e,t){var n=this.value()||0;this.counter||(this.counter=1),n=this._adjustValue(n+e*this._increment(this.counter));if(!this.spinning||this._trigger("spin",t,{value:n})!==!1)this._value(n),this.counter++},_increment:function(t){var n=this.options.incremental;return n?e.isFunction(n)?n(t):Math.floor(t*t*t/5e4-t*t/500+17*t/200+1):1},_precision:function(){var e=this._precisionOf(this.options.step);return this.options.min!==null&&(e=Math.max(e,this._precisionOf(this.options.min))),e},_precisionOf:function(e){var t=e.toString(),n=t.indexOf(".");return n===-1?0:t.length-n-1},_adjustValue:function(e){var t,n,r=this.options;return t=r.min!==null?r.min:0,n=e-t,n=Math.round(n/r.step)*r.step,e=t+n,e=parseFloat(e.toFixed(this._precision())),r.max!==null&&e>r.max?r.max:r.min!==null&&e1&&e.href.replace(r,"")===location.href.replace(r,"").replace(/\s/g,"%20")}var n=0,r=/#.*$/;e.widget("ui.tabs",{version:"1.9.2",delay:300,options:{active:null,collapsible:!1,event:"click",heightStyle:"content",hide:null,show:null,activate:null,beforeActivate:null,beforeLoad:null,load:null},_create:function(){var t=this,n=this.options,r=n.active,i=location.hash.substring(1);this.running=!1,this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all").toggleClass("ui-tabs-collapsible",n.collapsible).delegate(".ui-tabs-nav > li","mousedown"+this.eventNamespace,function(t){e(this).is(".ui-state-disabled")&&t.preventDefault()}).delegate(".ui-tabs-anchor","focus"+this.eventNamespace,function(){e(this).closest("li").is(".ui-state-disabled")&&this.blur()}),this._processTabs();if(r===null){i&&this.tabs.each(function(t,n){if(e(n).attr("aria-controls")===i)return r=t,!1}),r===null&&(r=this.tabs.index(this.tabs.filter(".ui-tabs-active")));if(r===null||r===-1)r=this.tabs.length?0:!1}r!==!1&&(r=this.tabs.index(this.tabs.eq(r)),r===-1&&(r=n.collapsible?!1:0)),n.active=r,!n.collapsible&&n.active===!1&&this.anchors.length&&(n.active=0),e.isArray(n.disabled)&&(n.disabled=e.unique(n.disabled.concat(e.map(this.tabs.filter(".ui-state-disabled"),function(e){return t.tabs.index(e)}))).sort()),this.options.active!==!1&&this.anchors.length?this.active=this._findActive(this.options.active):this.active=e(),this._refresh(),this.active.length&&this.load(n.active)},_getCreateEventData:function(){return{tab:this.active,panel:this.active.length?this._getPanelForTab(this.active):e()}},_tabKeydown:function(t){var n=e(this.document[0].activeElement).closest("li"),r=this.tabs.index(n),i=!0;if(this._handlePageNav(t))return;switch(t.keyCode){case e.ui.keyCode.RIGHT:case e.ui.keyCode.DOWN:r++;break;case e.ui.keyCode.UP:case e.ui.keyCode.LEFT:i=!1,r--;break;case e.ui.keyCode.END:r=this.anchors.length-1;break;case e.ui.keyCode.HOME:r=0;break;case e.ui.keyCode.SPACE:t.preventDefault(),clearTimeout(this.activating),this._activate(r);return;case e.ui.keyCode.ENTER:t.preventDefault(),clearTimeout(this.activating),this._activate(r===this.options.active?!1:r);return;default:return}t.preventDefault(),clearTimeout(this.activating),r=this._focusNextTab(r,i),t.ctrlKey||(n.attr("aria-selected","false"),this.tabs.eq(r).attr("aria-selected","true"),this.activating=this._delay(function(){this.option("active",r)},this.delay))},_panelKeydown:function(t){if(this._handlePageNav(t))return;t.ctrlKey&&t.keyCode===e.ui.keyCode.UP&&(t.preventDefault(),this.active.focus())},_handlePageNav:function(t){if(t.altKey&&t.keyCode===e.ui.keyCode.PAGE_UP)return this._activate(this._focusNextTab(this.options.active-1,!1)),!0;if(t.altKey&&t.keyCode===e.ui.keyCode.PAGE_DOWN)return this._activate(this._focusNextTab(this.options.active+1,!0)),!0},_findNextTab:function(t,n){function i(){return t>r&&(t=0),t<0&&(t=r),t}var r=this.tabs.length-1;while(e.inArray(i(),this.options.disabled)!==-1)t=n?t+1:t-1;return t},_focusNextTab:function(e,t){return e=this._findNextTab(e,t),this.tabs.eq(e).focus(),e},_setOption:function(e,t){if(e==="active"){this._activate(t);return}if(e==="disabled"){this._setupDisabled(t);return}this._super(e,t),e==="collapsible"&&(this.element.toggleClass("ui-tabs-collapsible",t),!t&&this.options.active===!1&&this._activate(0)),e==="event"&&this._setupEvents(t),e==="heightStyle"&&this._setupHeightStyle(t)},_tabId:function(e){return e.attr("aria-controls")||"ui-tabs-"+i()},_sanitizeSelector:function(e){return e?e.replace(/[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g,"\\$&"):""},refresh:function(){var t=this.options,n=this.tablist.children(":has(a[href])");t.disabled=e.map(n.filter(".ui-state-disabled"),function(e){return n.index(e)}),this._processTabs(),t.active===!1||!this.anchors.length?(t.active=!1,this.active=e()):this.active.length&&!e.contains(this.tablist[0],this.active[0])?this.tabs.length===t.disabled.length?(t.active=!1,this.active=e()):this._activate(this._findNextTab(Math.max(0,t.active-1),!1)):t.active=this.tabs.index(this.active),this._refresh()},_refresh:function(){this._setupDisabled(this.options.disabled),this._setupEvents(this.options.event),this._setupHeightStyle(this.options.heightStyle),this.tabs.not(this.active).attr({"aria-selected":"false",tabIndex:-1}),this.panels.not(this._getPanelForTab(this.active)).hide().attr({"aria-expanded":"false","aria-hidden":"true"}),this.active.length?(this.active.addClass("ui-tabs-active ui-state-active").attr({"aria-selected":"true",tabIndex:0}),this._getPanelForTab(this.active).show().attr({"aria-expanded":"true","aria-hidden":"false"})):this.tabs.eq(0).attr("tabIndex",0)},_processTabs:function(){var t=this;this.tablist=this._getList().addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").attr("role","tablist"),this.tabs=this.tablist.find("> li:has(a[href])").addClass("ui-state-default ui-corner-top").attr({role:"tab",tabIndex:-1}),this.anchors=this.tabs.map(function(){return e("a",this)[0]}).addClass("ui-tabs-anchor").attr({role:"presentation",tabIndex:-1}),this.panels=e(),this.anchors.each(function(n,r){var i,o,u,a=e(r).uniqueId().attr("id"),f=e(r).closest("li"),l=f.attr("aria-controls");s(r)?(i=r.hash,o=t.element.find(t._sanitizeSelector(i))):(u=t._tabId(f),i="#"+u,o=t.element.find(i),o.length||(o=t._createPanel(u),o.insertAfter(t.panels[n-1]||t.tablist)),o.attr("aria-live","polite")),o.length&&(t.panels=t.panels.add(o)),l&&f.data("ui-tabs-aria-controls",l),f.attr({"aria-controls":i.substring(1),"aria-labelledby":a}),o.attr("aria-labelledby",a)}),this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").attr("role","tabpanel")},_getList:function(){return this.element.find("ol,ul").eq(0)},_createPanel:function(t){return e("
    ").attr("id",t).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)},_setupDisabled:function(t){e.isArray(t)&&(t.length?t.length===this.anchors.length&&(t=!0):t=!1);for(var n=0,r;r=this.tabs[n];n++)t===!0||e.inArray(n,t)!==-1?e(r).addClass("ui-state-disabled").attr("aria-disabled","true"):e(r).removeClass("ui-state-disabled").removeAttr("aria-disabled");this.options.disabled=t},_setupEvents:function(t){var n={click:function(e){e.preventDefault()}};t&&e.each(t.split(" "),function(e,t){n[t]="_eventHandler"}),this._off(this.anchors.add(this.tabs).add(this.panels)),this._on(this.anchors,n),this._on(this.tabs,{keydown:"_tabKeydown"}),this._on(this.panels,{keydown:"_panelKeydown"}),this._focusable(this.tabs),this._hoverable(this.tabs)},_setupHeightStyle:function(t){var n,r,i=this.element.parent();t==="fill"?(e.support.minHeight||(r=i.css("overflow"),i.css("overflow","hidden")),n=i.height(),this.element.siblings(":visible").each(function(){var t=e(this),r=t.css("position");if(r==="absolute"||r==="fixed")return;n-=t.outerHeight(!0)}),r&&i.css("overflow",r),this.element.children().not(this.panels).each(function(){n-=e(this).outerHeight(!0)}),this.panels.each(function(){e(this).height(Math.max(0,n-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):t==="auto"&&(n=0,this.panels.each(function(){n=Math.max(n,e(this).height("").height())}).height(n))},_eventHandler:function(t){var n=this.options,r=this.active,i=e(t.currentTarget),s=i.closest("li"),o=s[0]===r[0],u=o&&n.collapsible,a=u?e():this._getPanelForTab(s),f=r.length?this._getPanelForTab(r):e(),l={oldTab:r,oldPanel:f,newTab:u?e():s,newPanel:a};t.preventDefault();if(s.hasClass("ui-state-disabled")||s.hasClass("ui-tabs-loading")||this.running||o&&!n.collapsible||this._trigger("beforeActivate",t,l)===!1)return;n.active=u?!1:this.tabs.index(s),this.active=o?e():s,this.xhr&&this.xhr.abort(),!f.length&&!a.length&&e.error("jQuery UI Tabs: Mismatching fragment identifier."),a.length&&this.load(this.tabs.index(s),t),this._toggle(t,l)},_toggle:function(t,n){function o(){r.running=!1,r._trigger("activate",t,n)}function u(){n.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),i.length&&r.options.show?r._show(i,r.options.show,o):(i.show(),o())}var r=this,i=n.newPanel,s=n.oldPanel;this.running=!0,s.length&&this.options.hide?this._hide(s,this.options.hide,function(){n.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),u()}):(n.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),s.hide(),u()),s.attr({"aria-expanded":"false","aria-hidden":"true"}),n.oldTab.attr("aria-selected","false"),i.length&&s.length?n.oldTab.attr("tabIndex",-1):i.length&&this.tabs.filter(function(){return e(this).attr("tabIndex")===0}).attr("tabIndex",-1),i.attr({"aria-expanded":"true","aria-hidden":"false"}),n.newTab.attr({"aria-selected":"true",tabIndex:0})},_activate:function(t){var n,r=this._findActive(t);if(r[0]===this.active[0])return;r.length||(r=this.active),n=r.find(".ui-tabs-anchor")[0],this._eventHandler({target:n,currentTarget:n,preventDefault:e.noop})},_findActive:function(t){return t===!1?e():this.tabs.eq(t)},_getIndex:function(e){return typeof e=="string"&&(e=this.anchors.index(this.anchors.filter("[href$='"+e+"']"))),e},_destroy:function(){this.xhr&&this.xhr.abort(),this.element.removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible"),this.tablist.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all").removeAttr("role"),this.anchors.removeClass("ui-tabs-anchor").removeAttr("role").removeAttr("tabIndex").removeData("href.tabs").removeData("load.tabs").removeUniqueId(),this.tabs.add(this.panels).each(function(){e.data(this,"ui-tabs-destroy")?e(this).remove():e(this).removeClass("ui-state-default ui-state-active ui-state-disabled ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel").removeAttr("tabIndex").removeAttr("aria-live").removeAttr("aria-busy").removeAttr("aria-selected").removeAttr("aria-labelledby").removeAttr("aria-hidden").removeAttr("aria-expanded").removeAttr("role")}),this.tabs.each(function(){var t=e(this),n=t.data("ui-tabs-aria-controls");n?t.attr("aria-controls",n):t.removeAttr("aria-controls")}),this.panels.show(),this.options.heightStyle!=="content"&&this.panels.css("height","")},enable:function(n){var r=this.options.disabled;if(r===!1)return;n===t?r=!1:(n=this._getIndex(n),e.isArray(r)?r=e.map(r,function(e){return e!==n?e:null}):r=e.map(this.tabs,function(e,t){return t!==n?t:null})),this._setupDisabled(r)},disable:function(n){var r=this.options.disabled;if(r===!0)return;if(n===t)r=!0;else{n=this._getIndex(n);if(e.inArray(n,r)!==-1)return;e.isArray(r)?r=e.merge([n],r).sort():r=[n]}this._setupDisabled(r)},load:function(t,n){t=this._getIndex(t);var r=this,i=this.tabs.eq(t),o=i.find(".ui-tabs-anchor"),u=this._getPanelForTab(i),a={tab:i,panel:u};if(s(o[0]))return;this.xhr=e.ajax(this._ajaxSettings(o,n,a)),this.xhr&&this.xhr.statusText!=="canceled"&&(i.addClass("ui-tabs-loading"),u.attr("aria-busy","true"),this.xhr.success(function(e){setTimeout(function(){u.html(e),r._trigger("load",n,a)},1)}).complete(function(e,t){setTimeout(function(){t==="abort"&&r.panels.stop(!1,!0),i.removeClass("ui-tabs-loading"),u.removeAttr("aria-busy"),e===r.xhr&&delete r.xhr},1)}))},_ajaxSettings:function(t,n,r){var i=this;return{url:t.attr("href"),beforeSend:function(t,s){return i._trigger("beforeLoad",n,e.extend({jqXHR:t,ajaxSettings:s},r))}}},_getPanelForTab:function(t){var n=e(t).attr("aria-controls");return this.element.find(this._sanitizeSelector("#"+n))}}),e.uiBackCompat!==!1&&(e.ui.tabs.prototype._ui=function(e,t){return{tab:e,panel:t,index:this.anchors.index(e)}},e.widget("ui.tabs",e.ui.tabs,{url:function(e,t){this.anchors.eq(e).attr("href",t)}}),e.widget("ui.tabs",e.ui.tabs,{options:{ajaxOptions:null,cache:!1},_create:function(){this._super();var t=this;this._on({tabsbeforeload:function(n,r){if(e.data(r.tab[0],"cache.tabs")){n.preventDefault();return}r.jqXHR.success(function(){t.options.cache&&e.data(r.tab[0],"cache.tabs",!0)})}})},_ajaxSettings:function(t,n,r){var i=this.options.ajaxOptions;return e.extend({},i,{error:function(e,t){try{i.error(e,t,r.tab.closest("li").index(),r.tab[0])}catch(n){}}},this._superApply(arguments))},_setOption:function(e,t){e==="cache"&&t===!1&&this.anchors.removeData("cache.tabs"),this._super(e,t)},_destroy:function(){this.anchors.removeData("cache.tabs"),this._super()},url:function(e){this.anchors.eq(e).removeData("cache.tabs"),this._superApply(arguments)}}),e.widget("ui.tabs",e.ui.tabs,{abort:function(){this.xhr&&this.xhr.abort()}}),e.widget("ui.tabs",e.ui.tabs,{options:{spinner:"Loading…"},_create:function(){this._super(),this._on({tabsbeforeload:function(e,t){if(e.target!==this.element[0]||!this.options.spinner)return;var n=t.tab.find("span"),r=n.html();n.html(this.options.spinner),t.jqXHR.complete(function(){n.html(r)})}})}}),e.widget("ui.tabs",e.ui.tabs,{options:{enable:null,disable:null},enable:function(t){var n=this.options,r;if(t&&n.disabled===!0||e.isArray(n.disabled)&&e.inArray(t,n.disabled)!==-1)r=!0;this._superApply(arguments),r&&this._trigger("enable",null,this._ui(this.anchors[t],this.panels[t]))},disable:function(t){var n=this.options,r;if(t&&n.disabled===!1||e.isArray(n.disabled)&&e.inArray(t,n.disabled)===-1)r=!0;this._superApply(arguments),r&&this._trigger("disable",null,this._ui(this.anchors[t],this.panels[t]))}}),e.widget("ui.tabs",e.ui.tabs,{options:{add:null,remove:null,tabTemplate:"
  • #{label}
  • "},add:function(n,r,i){i===t&&(i=this.anchors.length);var s,o,u=this.options,a=e(u.tabTemplate.replace(/#\{href\}/g,n).replace(/#\{label\}/g,r)),f=n.indexOf("#")?this._tabId(a):n.replace("#","");return a.addClass("ui-state-default ui-corner-top").data("ui-tabs-destroy",!0),a.attr("aria-controls",f),s=i>=this.tabs.length,o=this.element.find("#"+f),o.length||(o=this._createPanel(f),s?i>0?o.insertAfter(this.panels.eq(-1)):o.appendTo(this.element):o.insertBefore(this.panels[i])),o.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").hide(),s?a.appendTo(this.tablist):a.insertBefore(this.tabs[i]),u.disabled=e.map(u.disabled,function(e){return e>=i?++e:e}),this.refresh(),this.tabs.length===1&&u.active===!1&&this.option("active",0),this._trigger("add",null,this._ui(this.anchors[i],this.panels[i])),this},remove:function(t){t=this._getIndex(t);var n=this.options,r=this.tabs.eq(t).remove(),i=this._getPanelForTab(r).remove();return r.hasClass("ui-tabs-active")&&this.anchors.length>2&&this._activate(t+(t+1=t?--e:e}),this.refresh(),this._trigger("remove",null,this._ui(r.find("a")[0],i[0])),this}}),e.widget("ui.tabs",e.ui.tabs,{length:function(){return this.anchors.length}}),e.widget("ui.tabs",e.ui.tabs,{options:{idPrefix:"ui-tabs-"},_tabId:function(t){var n=t.is("li")?t.find("a[href]"):t;return n=n[0],e(n).closest("li").attr("aria-controls")||n.title&&n.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF\-]/g,"")||this.options.idPrefix+i()}}),e.widget("ui.tabs",e.ui.tabs,{options:{panelTemplate:"
    "},_createPanel:function(t){return e(this.options.panelTemplate).attr("id",t).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").data("ui-tabs-destroy",!0)}}),e.widget("ui.tabs",e.ui.tabs,{_create:function(){var e=this.options;e.active===null&&e.selected!==t&&(e.active=e.selected===-1?!1:e.selected),this._super(),e.selected=e.active,e.selected===!1&&(e.selected=-1)},_setOption:function(e,t){if(e!=="selected")return this._super(e,t);var n=this.options;this._super("active",t===-1?!1:t),n.selected=n.active,n.selected===!1&&(n.selected=-1)},_eventHandler:function(){this._superApply(arguments),this.options.selected=this.options.active,this.options.selected===!1&&(this.options.selected=-1)}}),e.widget("ui.tabs",e.ui.tabs,{options:{show:null,select:null},_create:function(){this._super(),this.options.active!==!1&&this._trigger("show",null,this._ui(this.active.find(".ui-tabs-anchor")[0],this._getPanelForTab(this.active)[0]))},_trigger:function(e,t,n){var r,i,s=this._superApply(arguments);return s?(e==="beforeActivate"?(r=n.newTab.length?n.newTab:n.oldTab,i=n.newPanel.length?n.newPanel:n.oldPanel,s=this._super("select",t,{tab:r.find(".ui-tabs-anchor")[0],panel:i[0],index:r.closest("li").index()})):e==="activate"&&n.newTab.length&&(s=this._super("show",t,{tab:n.newTab.find(".ui-tabs-anchor")[0],panel:n.newPanel[0],index:n.newTab.closest("li").index()})),s):!1}}),e.widget("ui.tabs",e.ui.tabs,{select:function(e){e=this._getIndex(e);if(e===-1){if(!this.options.collapsible||this.options.selected===-1)return;e=this.options.selected}this.anchors.eq(e).trigger(this.options.event+this.eventNamespace)}}),function(){var t=0;e.widget("ui.tabs",e.ui.tabs,{options:{cookie:null},_create:function(){var e=this.options,t;e.active==null&&e.cookie&&(t=parseInt(this._cookie(),10),t===-1&&(t=!1),e.active=t),this._super()},_cookie:function(n){var r=[this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+ ++t)];return arguments.length&&(r.push(n===!1?-1:n),r.push(this.options.cookie)),e.cookie.apply(null,r)},_refresh:function(){this._super(),this.options.cookie&&this._cookie(this.options.active,this.options.cookie)},_eventHandler:function(){this._superApply(arguments),this.options.cookie&&this._cookie(this.options.active,this.options.cookie)},_destroy:function(){this._super(),this.options.cookie&&this._cookie(null,this.options.cookie)}})}(),e.widget("ui.tabs",e.ui.tabs,{_trigger:function(t,n,r){var i=e.extend({},r);return t==="load"&&(i.panel=i.panel[0],i.tab=i.tab.find(".ui-tabs-anchor")[0]),this._super(t,n,i)}}),e.widget("ui.tabs",e.ui.tabs,{options:{fx:null},_getFx:function(){var t,n,r=this.options.fx;return r&&(e.isArray(r)?(t=r[0],n=r[1]):t=n=r),r?{show:n,hide:t}:null},_toggle:function(e,t){function o(){n.running=!1,n._trigger("activate",e,t)}function u(){t.newTab.closest("li").addClass("ui-tabs-active ui-state-active"),r.length&&s.show?r.animate(s.show,s.show.duration,function(){o()}):(r.show(),o())}var n=this,r=t.newPanel,i=t.oldPanel,s=this._getFx();if(!s)return this._super(e,t);n.running=!0,i.length&&s.hide?i.animate(s.hide,s.hide.duration,function(){t.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),u()}):(t.oldTab.closest("li").removeClass("ui-tabs-active ui-state-active"),i.hide(),u())}}))})(jQuery);(function(e){function n(t,n){var r=(t.attr("aria-describedby")||"").split(/\s+/);r.push(n),t.data("ui-tooltip-id",n).attr("aria-describedby",e.trim(r.join(" ")))}function r(t){var n=t.data("ui-tooltip-id"),r=(t.attr("aria-describedby")||"").split(/\s+/),i=e.inArray(n,r);i!==-1&&r.splice(i,1),t.removeData("ui-tooltip-id"),r=e.trim(r.join(" ")),r?t.attr("aria-describedby",r):t.removeAttr("aria-describedby")}var t=0;e.widget("ui.tooltip",{version:"1.9.2",options:{content:function(){return e(this).attr("title")},hide:!0,items:"[title]:not([disabled])",position:{my:"left top+15",at:"left bottom",collision:"flipfit flip"},show:!0,tooltipClass:null,track:!1,close:null,open:null},_create:function(){this._on({mouseover:"open",focusin:"open"}),this.tooltips={},this.parents={},this.options.disabled&&this._disable()},_setOption:function(t,n){var r=this;if(t==="disabled"){this[n?"_disable":"_enable"](),this.options[t]=n;return}this._super(t,n),t==="content"&&e.each(this.tooltips,function(e,t){r._updateContent(t)})},_disable:function(){var t=this;e.each(this.tooltips,function(n,r){var i=e.Event("blur");i.target=i.currentTarget=r[0],t.close(i,!0)}),this.element.find(this.options.items).andSelf().each(function(){var t=e(this);t.is("[title]")&&t.data("ui-tooltip-title",t.attr("title")).attr("title","")})},_enable:function(){this.element.find(this.options.items).andSelf().each(function(){var t=e(this);t.data("ui-tooltip-title")&&t.attr("title",t.data("ui-tooltip-title"))})},open:function(t){var n=this,r=e(t?t.target:this.element).closest(this.options.items);if(!r.length||r.data("ui-tooltip-id"))return;r.attr("title")&&r.data("ui-tooltip-title",r.attr("title")),r.data("ui-tooltip-open",!0),t&&t.type==="mouseover"&&r.parents().each(function(){var t=e(this),r;t.data("ui-tooltip-open")&&(r=e.Event("blur"),r.target=r.currentTarget=this,n.close(r,!0)),t.attr("title")&&(t.uniqueId(),n.parents[this.id]={element:this,title:t.attr("title")},t.attr("title",""))}),this._updateContent(r,t)},_updateContent:function(e,t){var n,r=this.options.content,i=this,s=t?t.type:null;if(typeof r=="string")return this._open(t,e,r);n=r.call(e[0],function(n){if(!e.data("ui-tooltip-open"))return;i._delay(function(){t&&(t.type=s),this._open(t,e,n)})}),n&&this._open(t,e,n)},_open:function(t,r,i){function f(e){a.of=e;if(s.is(":hidden"))return;s.position(a)}var s,o,u,a=e.extend({},this.options.position);if(!i)return;s=this._find(r);if(s.length){s.find(".ui-tooltip-content").html(i);return}r.is("[title]")&&(t&&t.type==="mouseover"?r.attr("title",""):r.removeAttr("title")),s=this._tooltip(r),n(r,s.attr("id")),s.find(".ui-tooltip-content").html(i),this.options.track&&t&&/^mouse/.test(t.type)?(this._on(this.document,{mousemove:f}),f(t)):s.position(e.extend({of:r},this.options.position)),s.hide(),this._show(s,this.options.show),this.options.show&&this.options.show.delay&&(u=setInterval(function(){s.is(":visible")&&(f(a.of),clearInterval(u))},e.fx.interval)),this._trigger("open",t,{tooltip:s}),o={keyup:function(t){if(t.keyCode===e.ui.keyCode.ESCAPE){var n=e.Event(t);n.currentTarget=r[0],this.close(n,!0)}},remove:function(){this._removeTooltip(s)}};if(!t||t.type==="mouseover")o.mouseleave="close";if(!t||t.type==="focusin")o.focusout="close";this._on(!0,r,o)},close:function(t){var n=this,i=e(t?t.currentTarget:this.element),s=this._find(i);if(this.closing)return;i.data("ui-tooltip-title")&&i.attr("title",i.data("ui-tooltip-title")),r(i),s.stop(!0),this._hide(s,this.options.hide,function(){n._removeTooltip(e(this))}),i.removeData("ui-tooltip-open"),this._off(i,"mouseleave focusout keyup"),i[0]!==this.element[0]&&this._off(i,"remove"),this._off(this.document,"mousemove"),t&&t.type==="mouseleave"&&e.each(this.parents,function(t,r){e(r.element).attr("title",r.title),delete n.parents[t]}),this.closing=!0,this._trigger("close",t,{tooltip:s}),this.closing=!1},_tooltip:function(n){var r="ui-tooltip-"+t++,i=e("
    ").attr({id:r,role:"tooltip"}).addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||""));return e("
    ").addClass("ui-tooltip-content").appendTo(i),i.appendTo(this.document[0].body),e.fn.bgiframe&&i.bgiframe(),this.tooltips[r]=n,i},_find:function(t){var n=t.data("ui-tooltip-id");return n?e("#"+n):e()},_removeTooltip:function(e){e.remove(),delete this.tooltips[e.attr("id")]},_destroy:function(){var t=this;e.each(this.tooltips,function(n,r){var i=e.Event("blur");i.target=i.currentTarget=r[0],t.close(i,!0),e("#"+n).remove(),r.data("ui-tooltip-title")&&(r.attr("title",r.data("ui-tooltip-title")),r.removeData("ui-tooltip-title"))})}})})(jQuery); \ No newline at end of file +(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(t,s){var n,a,o,r=t.nodeName.toLowerCase();return"area"===r?(n=t.parentNode,a=n.name,t.href&&a&&"map"===n.nodeName.toLowerCase()?(o=e("img[usemap='#"+a+"']")[0],!!o&&i(o)):!1):(/input|select|textarea|button|object/.test(r)?!t.disabled:"a"===r?t.href||s:s)&&i(t)}function i(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}function s(e){for(var t,i;e.length&&e[0]!==document;){if(t=e.css("position"),("absolute"===t||"relative"===t||"fixed"===t)&&(i=parseInt(e.css("zIndex"),10),!isNaN(i)&&0!==i))return i;e=e.parent()}return 0}function n(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},e.extend(this._defaults,this.regional[""]),this.regional.en=e.extend(!0,{},this.regional[""]),this.regional["en-US"]=e.extend(!0,{},this.regional.en),this.dpDiv=a(e("
    "))}function a(t){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return t.delegate(i,"mouseout",function(){e(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).removeClass("ui-datepicker-next-hover")}).delegate(i,"mouseover",o)}function o(){e.datepicker._isDisabledDatepicker(v.inline?v.dpDiv.parent()[0]:v.input[0])||(e(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),e(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).addClass("ui-datepicker-next-hover"))}function r(t,i){e.extend(t,i);for(var s in i)null==i[s]&&(t[s]=i[s]);return t}function h(e){return function(){var t=this.element.val();e.apply(this,arguments),this._refresh(),t!==this.element.val()&&this._trigger("change")}}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.2",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(t){var i=this.css("position"),s="absolute"===i,n=t?/(auto|scroll|hidden)/:/(auto|scroll)/,a=this.parents().filter(function(){var t=e(this);return s&&"static"===t.css("position")?!1:n.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==i&&a.length?a:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(i){return!!e.data(i,t)}}):function(t,i,s){return!!e.data(t,s[3])},focusable:function(i){return t(i,!isNaN(e.attr(i,"tabindex")))},tabbable:function(i){var s=e.attr(i,"tabindex"),n=isNaN(s);return(n||s>=0)&&t(i,!n)}}),e("").outerWidth(1).jquery||e.each(["Width","Height"],function(t,i){function s(t,i,s,a){return e.each(n,function(){i-=parseFloat(e.css(t,"padding"+this))||0,s&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),a&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],a=i.toLowerCase(),o={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+i]=function(t){return void 0===t?o["inner"+i].call(this):this.each(function(){e(this).css(a,s(this,t)+"px")})},e.fn["outer"+i]=function(t,n){return"number"!=typeof t?o["outer"+i].call(this,t):this.each(function(){e(this).css(a,s(this,t,!0,n)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(i,s){return"number"==typeof i?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),s&&s.call(t)},i)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var i,s,n=e(this[0]);n.length&&n[0]!==document;){if(i=n.css("position"),("absolute"===i||"relative"===i||"fixed"===i)&&(s=parseInt(n.css("zIndex"),10),!isNaN(s)&&0!==s))return s;n=n.parent()}return 0}}),e.ui.plugin={add:function(t,i,s){var n,a=e.ui[t].prototype;for(n in s)a.plugins[n]=a.plugins[n]||[],a.plugins[n].push([i,s[n]])},call:function(e,t,i,s){var n,a=e.plugins[t];if(a&&(s||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(n=0;a.length>n;n++)e.options[a[n][0]]&&a[n][1].apply(e.element,i)}};var l=0,u=Array.prototype.slice;e.cleanData=function(t){return function(i){var s,n,a;for(a=0;null!=(n=i[a]);a++)try{s=e._data(n,"events"),s&&s.remove&&e(n).triggerHandler("remove")}catch(o){}t(i)}}(e.cleanData),e.widget=function(t,i,s){var n,a,o,r,h={},l=t.split(".")[0];return t=t.split(".")[1],n=l+"-"+t,s||(s=i,i=e.Widget),e.expr[":"][n.toLowerCase()]=function(t){return!!e.data(t,n)},e[l]=e[l]||{},a=e[l][t],o=e[l][t]=function(e,t){return this._createWidget?(arguments.length&&this._createWidget(e,t),void 0):new o(e,t)},e.extend(o,a,{version:s.version,_proto:e.extend({},s),_childConstructors:[]}),r=new i,r.options=e.widget.extend({},r.options),e.each(s,function(t,s){return e.isFunction(s)?(h[t]=function(){var e=function(){return i.prototype[t].apply(this,arguments)},n=function(e){return i.prototype[t].apply(this,e)};return function(){var t,i=this._super,a=this._superApply;return this._super=e,this._superApply=n,t=s.apply(this,arguments),this._super=i,this._superApply=a,t}}(),void 0):(h[t]=s,void 0)}),o.prototype=e.widget.extend(r,{widgetEventPrefix:a?r.widgetEventPrefix||t:t},h,{constructor:o,namespace:l,widgetName:t,widgetFullName:n}),a?(e.each(a._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete a._childConstructors):i._childConstructors.push(o),e.widget.bridge(t,o),o},e.widget.extend=function(t){for(var i,s,n=u.call(arguments,1),a=0,o=n.length;o>a;a++)for(i in n[a])s=n[a][i],n[a].hasOwnProperty(i)&&void 0!==s&&(t[i]=e.isPlainObject(s)?e.isPlainObject(t[i])?e.widget.extend({},t[i],s):e.widget.extend({},s):s);return t},e.widget.bridge=function(t,i){var s=i.prototype.widgetFullName||t;e.fn[t]=function(n){var a="string"==typeof n,o=u.call(arguments,1),r=this;return n=!a&&o.length?e.widget.extend.apply(null,[n].concat(o)):n,a?this.each(function(){var i,a=e.data(this,s);return"instance"===n?(r=a,!1):a?e.isFunction(a[n])&&"_"!==n.charAt(0)?(i=a[n].apply(a,o),i!==a&&void 0!==i?(r=i&&i.jquery?r.pushStack(i.get()):i,!1):void 0):e.error("no such method '"+n+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; "+"attempted to call method '"+n+"'")}):this.each(function(){var t=e.data(this,s);t?(t.option(n||{}),t._init&&t._init()):e.data(this,s,new i(n,this))}),r}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
    ",options:{disabled:!1,create:null},_createWidget:function(t,i){i=e(i||this.defaultElement||this)[0],this.element=e(i),this.uuid=l++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=e(),this.hoverable=e(),this.focusable=e(),i!==this&&(e.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===i&&this.destroy()}}),this.document=e(i.style?i.ownerDocument:i.document||i),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,i){var s,n,a,o=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(o={},s=t.split("."),t=s.shift(),s.length){for(n=o[t]=e.widget.extend({},this.options[t]),a=0;s.length-1>a;a++)n[s[a]]=n[s[a]]||{},n=n[s[a]];if(t=s.pop(),1===arguments.length)return void 0===n[t]?null:n[t];n[t]=i}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];o[t]=i}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,i,s){var n,a=this;"boolean"!=typeof t&&(s=i,i=t,t=!1),s?(i=n=e(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),e.each(s,function(s,o){function r(){return t||a.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?a[o]:o).apply(a,arguments):void 0}"string"!=typeof o&&(r.guid=o.guid=o.guid||r.guid||e.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+a.eventNamespace,u=h[2];u?n.delegate(u,l,r):i.bind(l,r)})},_off:function(t,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(i).undelegate(i),this.bindings=e(this.bindings.not(t).get()),this.focusable=e(this.focusable.not(t).get()),this.hoverable=e(this.hoverable.not(t).get())},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var n,a,o=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],a=i.originalEvent)for(n in a)n in i||(i[n]=a[n]);return this.element.trigger(i,s),!(e.isFunction(o)&&o.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,n,a){"string"==typeof n&&(n={effect:n});var o,r=n?n===!0||"number"==typeof n?i:n.effect||i:t;n=n||{},"number"==typeof n&&(n={duration:n}),o=!e.isEmptyObject(n),n.complete=a,n.delay&&s.delay(n.delay),o&&e.effects&&e.effects.effect[r]?s[t](n):r!==t&&s[r]?s[r](n.duration,n.easing,a):s.queue(function(i){e(this)[t](),a&&a.call(s[0]),i()})}}),e.widget;var d=!1;e(document).mouseup(function(){d=!1}),e.widget("ui.mouse",{version:"1.11.2",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(i){return!0===e.data(i.target,t.widgetName+".preventClickEvent")?(e.removeData(i.target,t.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(!d){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var i=this,s=1===t.which,n="string"==typeof this.options.cancel&&t.target.nodeName?e(t.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(t)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(t)!==!1,!this._mouseStarted)?(t.preventDefault(),!0):(!0===e.data(t.target,this.widgetName+".preventClickEvent")&&e.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return i._mouseMove(e)},this._mouseUpDelegate=function(e){return i._mouseUp(e)},this.document.bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),d=!0,!0)):!0}},_mouseMove:function(t){if(this._mouseMoved){if(e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button)return this._mouseUp(t);if(!t.which)return this._mouseUp(t)}return(t.which||t.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted)},_mouseUp:function(t){return this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),d=!1,!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),function(){function t(e,t,i){return[parseFloat(e[0])*(p.test(e[0])?t/100:1),parseFloat(e[1])*(p.test(e[1])?i/100:1)]}function i(t,i){return parseInt(e.css(t,i),10)||0}function s(t){var i=t[0];return 9===i.nodeType?{width:t.width(),height:t.height(),offset:{top:0,left:0}}:e.isWindow(i)?{width:t.width(),height:t.height(),offset:{top:t.scrollTop(),left:t.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:t.outerWidth(),height:t.outerHeight(),offset:t.offset()}}e.ui=e.ui||{};var n,a,o=Math.max,r=Math.abs,h=Math.round,l=/left|center|right/,u=/top|center|bottom/,d=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,p=/%$/,f=e.fn.position;e.position={scrollbarWidth:function(){if(void 0!==n)return n;var t,i,s=e("
    "),a=s.children()[0];return e("body").append(s),t=a.offsetWidth,s.css("overflow","scroll"),i=a.offsetWidth,t===i&&(i=s[0].clientWidth),s.remove(),n=t-i},getScrollInfo:function(t){var i=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),s=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),n="scroll"===i||"auto"===i&&t.widthi?"left":t>0?"right":"center",vertical:0>a?"top":s>0?"bottom":"middle"};d>m&&m>r(t+i)&&(h.horizontal="center"),c>g&&g>r(s+a)&&(h.vertical="middle"),h.important=o(r(t),r(i))>o(r(s),r(a))?"horizontal":"vertical",n.using.call(this,e,h)}),u.offset(e.extend(M,{using:l}))})},e.ui.position={fit:{left:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=e.left-t.collisionPosition.marginLeft,h=n-r,l=r+t.collisionWidth-a-n;t.collisionWidth>a?h>0&&0>=l?(i=e.left+h+t.collisionWidth-a-n,e.left+=h-i):e.left=l>0&&0>=h?n:h>l?n+a-t.collisionWidth:n:h>0?e.left+=h:l>0?e.left-=l:e.left=o(e.left-r,e.left)},top:function(e,t){var i,s=t.within,n=s.isWindow?s.scrollTop:s.offset.top,a=t.within.height,r=e.top-t.collisionPosition.marginTop,h=n-r,l=r+t.collisionHeight-a-n;t.collisionHeight>a?h>0&&0>=l?(i=e.top+h+t.collisionHeight-a-n,e.top+=h-i):e.top=l>0&&0>=h?n:h>l?n+a-t.collisionHeight:n:h>0?e.top+=h:l>0?e.top-=l:e.top=o(e.top-r,e.top)}},flip:{left:function(e,t){var i,s,n=t.within,a=n.offset.left+n.scrollLeft,o=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=e.left-t.collisionPosition.marginLeft,u=l-h,d=l+t.collisionWidth-o-h,c="left"===t.my[0]?-t.elemWidth:"right"===t.my[0]?t.elemWidth:0,p="left"===t.at[0]?t.targetWidth:"right"===t.at[0]?-t.targetWidth:0,f=-2*t.offset[0];0>u?(i=e.left+c+p+f+t.collisionWidth-o-a,(0>i||r(u)>i)&&(e.left+=c+p+f)):d>0&&(s=e.left-t.collisionPosition.marginLeft+c+p+f-h,(s>0||d>r(s))&&(e.left+=c+p+f))},top:function(e,t){var i,s,n=t.within,a=n.offset.top+n.scrollTop,o=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=e.top-t.collisionPosition.marginTop,u=l-h,d=l+t.collisionHeight-o-h,c="top"===t.my[1],p=c?-t.elemHeight:"bottom"===t.my[1]?t.elemHeight:0,f="top"===t.at[1]?t.targetHeight:"bottom"===t.at[1]?-t.targetHeight:0,m=-2*t.offset[1];0>u?(s=e.top+p+f+m+t.collisionHeight-o-a,e.top+p+f+m>u&&(0>s||r(u)>s)&&(e.top+=p+f+m)):d>0&&(i=e.top-t.collisionPosition.marginTop+p+f+m-h,e.top+p+f+m>d&&(i>0||d>r(i))&&(e.top+=p+f+m))}},flipfit:{left:function(){e.ui.position.flip.left.apply(this,arguments),e.ui.position.fit.left.apply(this,arguments)},top:function(){e.ui.position.flip.top.apply(this,arguments),e.ui.position.fit.top.apply(this,arguments)}}},function(){var t,i,s,n,o,r=document.getElementsByTagName("body")[0],h=document.createElement("div");t=document.createElement(r?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},r&&e.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(o in s)t.style[o]=s[o];t.appendChild(h),i=r||document.documentElement,i.insertBefore(t,i.firstChild),h.style.cssText="position: absolute; left: 10.7432222px;",n=e(h).offset().left,a=n>10&&11>n,t.innerHTML="",i.removeChild(t)}()}(),e.ui.position,e.widget("ui.accordion",{version:"1.11.2",options:{active:0,animate:{},collapsible:!1,event:"click",header:"> li > :first-child,> :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var t=this.options;this.prevShow=this.prevHide=e(),this.element.addClass("ui-accordion ui-widget ui-helper-reset").attr("role","tablist"),t.collapsible||t.active!==!1&&null!=t.active||(t.active=0),this._processPanels(),0>t.active&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():e()}},_createIcons:function(){var t=this.options.icons;t&&(e("").addClass("ui-accordion-header-icon ui-icon "+t.header).prependTo(this.headers),this.active.children(".ui-accordion-header-icon").removeClass(t.header).addClass(t.activeHeader),this.headers.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.removeClass("ui-accordion-icons").children(".ui-accordion-header-icon").remove()},_destroy:function(){var e;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.removeClass("ui-accordion-header ui-accordion-header-active ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("aria-controls").removeAttr("tabIndex").removeUniqueId(),this._destroyIcons(),e=this.headers.next().removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled").css("display","").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&e.css("height","")},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):("event"===e&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(t)),this._super(e,t),"collapsible"!==e||t||this.options.active!==!1||this._activate(0),"icons"===e&&(this._destroyIcons(),t&&this._createIcons()),"disabled"===e&&(this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this.headers.add(this.headers.next()).toggleClass("ui-state-disabled",!!t)),void 0)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var i=e.ui.keyCode,s=this.headers.length,n=this.headers.index(t.target),a=!1;switch(t.keyCode){case i.RIGHT:case i.DOWN:a=this.headers[(n+1)%s];break;case i.LEFT:case i.UP:a=this.headers[(n-1+s)%s];break;case i.SPACE:case i.ENTER:this._eventHandler(t);break;case i.HOME:a=this.headers[0];break;case i.END:a=this.headers[s-1]}a&&(e(t.target).attr("tabIndex",-1),e(a).attr("tabIndex",0),a.focus(),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===e.ui.keyCode.UP&&t.ctrlKey&&e(t.currentTarget).prev().focus()},refresh:function(){var t=this.options;this._processPanels(),t.active===!1&&t.collapsible===!0||!this.headers.length?(t.active=!1,this.active=e()):t.active===!1?this._activate(0):this.active.length&&!e.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=e()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){var e=this.headers,t=this.panels;this.headers=this.element.find(this.options.header).addClass("ui-accordion-header ui-state-default ui-corner-all"),this.panels=this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").filter(":not(.ui-accordion-content-active)").hide(),t&&(this._off(e.not(this.headers)),this._off(t.not(this.panels)))},_refresh:function(){var t,i=this.options,s=i.heightStyle,n=this.element.parent();this.active=this._findActive(i.active).addClass("ui-accordion-header-active ui-state-active ui-corner-top").removeClass("ui-corner-all"),this.active.next().addClass("ui-accordion-content-active").show(),this.headers.attr("role","tab").each(function(){var t=e(this),i=t.uniqueId().attr("id"),s=t.next(),n=s.uniqueId().attr("id");t.attr("aria-controls",n),s.attr("aria-labelledby",i)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(i.event),"fill"===s?(t=n.height(),this.element.siblings(":visible").each(function(){var i=e(this),s=i.css("position");"absolute"!==s&&"fixed"!==s&&(t-=i.outerHeight(!0))}),this.headers.each(function(){t-=e(this).outerHeight(!0)}),this.headers.next().each(function(){e(this).height(Math.max(0,t-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===s&&(t=0,this.headers.next().each(function(){t=Math.max(t,e(this).css("height","").height())}).height(t))},_activate:function(t){var i=this._findActive(t)[0];i!==this.active[0]&&(i=i||this.active[0],this._eventHandler({target:i,currentTarget:i,preventDefault:e.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):e()},_setupEvents:function(t){var i={keydown:"_keydown"};t&&e.each(t.split(" "),function(e,t){i[t]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var i=this.options,s=this.active,n=e(t.currentTarget),a=n[0]===s[0],o=a&&i.collapsible,r=o?e():n.next(),h=s.next(),l={oldHeader:s,oldPanel:h,newHeader:o?e():n,newPanel:r};t.preventDefault(),a&&!i.collapsible||this._trigger("beforeActivate",t,l)===!1||(i.active=o?!1:this.headers.index(n),this.active=a?e():n,this._toggle(l),s.removeClass("ui-accordion-header-active ui-state-active"),i.icons&&s.children(".ui-accordion-header-icon").removeClass(i.icons.activeHeader).addClass(i.icons.header),a||(n.removeClass("ui-corner-all").addClass("ui-accordion-header-active ui-state-active ui-corner-top"),i.icons&&n.children(".ui-accordion-header-icon").removeClass(i.icons.header).addClass(i.icons.activeHeader),n.next().addClass("ui-accordion-content-active")))},_toggle:function(t){var i=t.newPanel,s=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=i,this.prevHide=s,this.options.animate?this._animate(i,s,t):(s.hide(),i.show(),this._toggleComplete(t)),s.attr({"aria-hidden":"true"}),s.prev().attr("aria-selected","false"),i.length&&s.length?s.prev().attr({tabIndex:-1,"aria-expanded":"false"}):i.length&&this.headers.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),i.attr("aria-hidden","false").prev().attr({"aria-selected":"true",tabIndex:0,"aria-expanded":"true"})},_animate:function(e,t,i){var s,n,a,o=this,r=0,h=e.length&&(!t.length||e.index()",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},items:"> *",menus:"ul",position:{my:"left-1 top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item":function(e){e.preventDefault()},"click .ui-menu-item":function(t){var i=e(t.target);!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(t),t.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(t):!this.element.is(":focus")&&e(this.document[0].activeElement).closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(t){if(!this.previousFilter){var i=e(t.currentTarget);i.siblings(".ui-state-active").removeClass("ui-state-active"),this.focus(t,i) +}},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(e,t){var i=this.active||this.element.find(this.options.items).eq(0);t||this.focus(e,i)},blur:function(t){this._delay(function(){e.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){this._closeOnDocumentClick(e)&&this.collapseAll(e),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-menu-icons ui-front").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").removeUniqueId().removeClass("ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var t=e(this);t.data("ui-menu-submenu-carat")&&t.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(t){var i,s,n,a,o=!0;switch(t.keyCode){case e.ui.keyCode.PAGE_UP:this.previousPage(t);break;case e.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case e.ui.keyCode.HOME:this._move("first","first",t);break;case e.ui.keyCode.END:this._move("last","last",t);break;case e.ui.keyCode.UP:this.previous(t);break;case e.ui.keyCode.DOWN:this.next(t);break;case e.ui.keyCode.LEFT:this.collapse(t);break;case e.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case e.ui.keyCode.ENTER:case e.ui.keyCode.SPACE:this._activate(t);break;case e.ui.keyCode.ESCAPE:this.collapse(t);break;default:o=!1,s=this.previousFilter||"",n=String.fromCharCode(t.keyCode),a=!1,clearTimeout(this.filterTimer),n===s?a=!0:n=s+n,i=this._filterMenuItems(n),i=a&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(n=String.fromCharCode(t.keyCode),i=this._filterMenuItems(n)),i.length?(this.focus(t,i),this.previousFilter=n,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}o&&t.preventDefault()},_activate:function(e){this.active.is(".ui-state-disabled")||(this.active.is("[aria-haspopup='true']")?this.expand(e):this.select(e))},refresh:function(){var t,i,s=this,n=this.options.icons.submenu,a=this.element.find(this.options.menus);this.element.toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length),a.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-front").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=e(this),i=t.parent(),s=e("").addClass("ui-menu-icon ui-icon "+n).data("ui-menu-submenu-carat",!0);i.attr("aria-haspopup","true").prepend(s),t.attr("aria-labelledby",i.attr("id"))}),t=a.add(this.element),i=t.find(this.options.items),i.not(".ui-menu-item").each(function(){var t=e(this);s._isDivider(t)&&t.addClass("ui-widget-content ui-menu-divider")}),i.not(".ui-menu-item, .ui-menu-divider").addClass("ui-menu-item").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!e.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(e,t){"icons"===e&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(t.submenu),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},focus:function(e,t){var i,s;this.blur(e,e&&"focus"===e.type),this._scrollIntoView(t),this.active=t.first(),s=this.active.addClass("ui-state-focus").removeClass("ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),this.active.parent().closest(".ui-menu-item").addClass("ui-state-active"),e&&"keydown"===e.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=t.children(".ui-menu"),i.length&&e&&/^mouse/.test(e.type)&&this._startOpening(i),this.activeMenu=t.parent(),this._trigger("focus",e,{item:t})},_scrollIntoView:function(t){var i,s,n,a,o,r;this._hasScroll()&&(i=parseFloat(e.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(e.css(this.activeMenu[0],"paddingTop"))||0,n=t.offset().top-this.activeMenu.offset().top-i-s,a=this.activeMenu.scrollTop(),o=this.activeMenu.height(),r=t.outerHeight(),0>n?this.activeMenu.scrollTop(a+n):n+r>o&&this.activeMenu.scrollTop(a+n-o+r))},blur:function(e,t){t||clearTimeout(this.timer),this.active&&(this.active.removeClass("ui-state-focus"),this.active=null,this._trigger("blur",e,{item:this.active}))},_startOpening:function(e){clearTimeout(this.timer),"true"===e.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(e)},this.delay))},_open:function(t){var i=e.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(t.parents(".ui-menu")).hide().attr("aria-hidden","true"),t.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(t,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:e(t&&t.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(t),this.activeMenu=s},this.delay)},_close:function(e){e||(e=this.active?this.active.parent():this.element),e.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find(".ui-state-active").not(".ui-state-focus").removeClass("ui-state-active")},_closeOnDocumentClick:function(t){return!e(t.target).closest(".ui-menu").length},_isDivider:function(e){return!/[^\-\u2014\u2013\s]/.test(e.text())},collapse:function(e){var t=this.active&&this.active.parent().closest(".ui-menu-item",this.element);t&&t.length&&(this._close(),this.focus(e,t))},expand:function(e){var t=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();t&&t.length&&(this._open(t.parent()),this._delay(function(){this.focus(e,t)}))},next:function(e){this._move("next","first",e)},previous:function(e){this._move("prev","last",e)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(e,t,i){var s;this.active&&(s="first"===e||"last"===e?this.active["first"===e?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[e+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[t]()),this.focus(i,s)},nextPage:function(t){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=e(this),0>i.offset().top-s-n}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(t),void 0)},previousPage:function(t){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=e(this),i.offset().top-s+n>0}),this.focus(t,i)):this.focus(t,this.activeMenu.find(this.options.items).first())),void 0):(this.next(t),void 0)},_hasScroll:function(){return this.element.outerHeight()",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var t,i,s,n=this.element[0].nodeName.toLowerCase(),a="textarea"===n,o="input"===n;this.isMultiLine=a?!0:o?!1:this.element.prop("isContentEditable"),this.valueMethod=this.element[a||o?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return t=!0,s=!0,i=!0,void 0;t=!1,s=!1,i=!1;var a=e.ui.keyCode;switch(n.keyCode){case a.PAGE_UP:t=!0,this._move("previousPage",n);break;case a.PAGE_DOWN:t=!0,this._move("nextPage",n);break;case a.UP:t=!0,this._keyEvent("previous",n);break;case a.DOWN:t=!0,this._keyEvent("next",n);break;case a.ENTER:this.menu.active&&(t=!0,n.preventDefault(),this.menu.select(n));break;case a.TAB:this.menu.active&&this.menu.select(n);break;case a.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(t)return t=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),void 0;if(!i){var n=e.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(e){return s?(s=!1,e.preventDefault(),void 0):(this._searchTimeout(e),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(e),this._change(e),void 0)}}),this._initSource(),this.menu=e("
    * The Group indicators can be also injected via custom soap definitions as XML node into WSDL structure. * - * In the following example, class Foo will create a XML node ... with children attributes expected in pre-defined order. + * In the following example, class Foo will create a XML node <xsd:Foo><xsd:sequence> ... </xsd:sequence></xsd:Foo> with children attributes expected in pre-defined order. *
      * / *
      *   * @soap-indicator sequence
    @@ -142,7 +142,7 @@
      *     public $date_of_birth;
      * }
      * 
    - * In the example above, WSDL generator would inject under XML node the code block defined by @soap-wsdl lines. + * In the example above, WSDL generator would inject under XML node <xsd:User> the code block defined by @soap-wsdl lines. * * By inserting into SOAP URL link the parameter "?makedoc", WSDL generator will output human-friendly overview of all complex data types rather than XML WSDL file. * Each complex type is described in a separate HTML table and recognizes also the '@example' PHPDoc tag. See {@link buildHtmlDocs()}. @@ -153,6 +153,10 @@ */ class CWsdlGenerator extends CComponent { + const STYLE_RPC = 'rpc'; + const STYLE_DOCUMENT = 'document'; + const USE_ENCODED = 'encoded'; + const USE_LITERAL = 'literal'; /** * @var string the namespace to be used in the generated WSDL. * If not set, it defaults to the name of the class that WSDL is generated upon. @@ -163,6 +167,24 @@ class CWsdlGenerator extends CComponent * If not set, it defaults to "urn:{$className}wsdl". */ public $serviceName; + /** + * @var array + * soap:body operation style options + */ + public $operationBodyStyle = array( + 'use' => self::USE_ENCODED, + 'encodingStyle' => 'http://schemas.xmlsoap.org/soap/encoding/', + ); + /** + * @var array + * soap:operation style + */ + public $bindingStyle = self::STYLE_RPC; + /** + * @var string + * soap:operation transport + */ + public $bindingTransport = 'http://schemas.xmlsoap.org/soap/http'; protected static $typeMap=array( 'string'=>'xsd:string', @@ -194,6 +216,11 @@ class CWsdlGenerator extends CComponent */ protected $types; + /** + * @var array + */ + protected $elements; + /** * @var array Map of request and response types for all operations. */ @@ -210,6 +237,7 @@ public function generateWsdl($className, $serviceUrl, $encoding='UTF-8') { $this->operations=array(); $this->types=array(); + $this->elements=array(); $this->messages=array(); if($this->serviceName===null) $this->serviceName=$className; @@ -245,25 +273,90 @@ protected function processMethod($method) $comment=preg_replace('/^\s*\**(\s*?$|\s*)/m','',$comment); $params=$method->getParameters(); $message=array(); + $headers=array(); $n=preg_match_all('/^@param\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches); if($n>count($params)) $n=count($params); + if ($this->bindingStyle == self::STYLE_RPC) + { + for($i=0;$i<$n;++$i) + $message[$params[$i]->getName()]=array( + 'type'=>$this->processType($matches[1][$i]), + 'doc'=>trim($matches[3][$i]), + ); + } + else + { + $this->elements[$methodName] = array(); + for($i=0;$i<$n;++$i) + $this->elements[$methodName][$params[$i]->getName()]=array( + 'type'=>$this->processType($matches[1][$i]), + 'nillable'=>$params[$i]->isOptional(), + ); + $message['parameters'] = array('element'=>'tns:'.$methodName); + } + + $this->messages[$methodName.'In']=$message; + + $n=preg_match_all('/^@header\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches); for($i=0;$i<$n;++$i) - $message[$params[$i]->getName()]=array($this->processType($matches[1][$i]), trim($matches[3][$i])); // name => type, doc + { + $name = $matches[1][$i]; + $type = $this->processType($matches[1][$i]); + $doc = trim($matches[3][$i]); + if ($this->bindingStyle == self::STYLE_RPC) + { + $headers[$name]=array($type,$doc); + } + else + { + $this->elements[$name][$name]=array('type'=>$type); + $headers[$name] = array('element'=>$type); + } + } - $this->messages[$methodName.'Request']=$message; + if ($headers !== array()) + { + $this->messages[$methodName.'Headers']=$headers; + $headerKeys = array_keys($headers); + $firstHeaderKey = reset($headerKeys); + $firstHeader = $headers[$firstHeaderKey]; + } + else + { + $firstHeader = null; + } - if(preg_match('/^@return\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches)) - $return=array($this->processType($matches[1]),trim($matches[2])); // type, doc + if ($this->bindingStyle == self::STYLE_RPC) + { + if(preg_match('/^@return\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches)) + $return=array( + 'type'=>$this->processType($matches[1]), + 'doc'=>trim($matches[2]), + ); + else + $return=null; + $this->messages[$methodName.'Out']=array('return'=>$return); + } else - $return=null; - $this->messages[$methodName.'Response']=array('return'=>$return); + { + if(preg_match('/^@return\s+([\w\.]+(\[\s*\])?)\s*?(.*)$/im',$comment,$matches)) + { + $this->elements[$methodName.'Response'][$methodName.'Result']=array( + 'type'=>$this->processType($matches[1]), + ); + } + $this->messages[$methodName.'Out']=array('parameters'=>array('element'=>'tns:'.$methodName.'Response')); + } if(preg_match('/^\/\*+\s*([^@]*?)\n@/s',$comment,$matches)) $doc=trim($matches[1]); else $doc=''; - $this->operations[$methodName]=$doc; + $this->operations[$methodName]=array( + 'doc'=>$doc, + 'headers'=>$firstHeader === null ? null : array('input'=>array($methodName.'Headers', $firstHeaderKey)), + ); } /** @@ -435,7 +528,7 @@ protected function buildDOM($serviceUrl,$encoding) */ protected function addTypes($dom) { - if($this->types===array()) + if($this->types===array() && $this->elements===array()) return; $types=$dom->createElement('wsdl:types'); $schema=$dom->createElement('xsd:schema'); @@ -451,21 +544,33 @@ protected function addTypes($dom) $complexType->setAttribute('name',substr($xmlType,4)); else $complexType->setAttribute('name',$xmlType); - $complexContent=$dom->createElement('xsd:complexContent'); - $restriction=$dom->createElement('xsd:restriction'); - $restriction->setAttribute('base','soap-enc:Array'); - $attribute=$dom->createElement('xsd:attribute'); - $attribute->setAttribute('ref','soap-enc:arrayType'); - $attribute->setAttribute('wsdl:arrayType',substr($xmlType,0,strlen($xmlType)-5).'[]'); - - $arrayType=($dppos=strpos($xmlType,':')) !==false ? substr($xmlType,$dppos + 1) : $xmlType; // strip namespace, if any - $arrayType=substr($arrayType,0,-5); // strip 'Array' from name - $arrayType=(isset(self::$typeMap[$arrayType]) ? 'xsd:' : 'tns:') .$arrayType.'[]'; - $attribute->setAttribute('wsdl:arrayType',$arrayType); - - $restriction->appendChild($attribute); - $complexContent->appendChild($restriction); - $complexType->appendChild($complexContent); + + $arrayType = ($dppos=strpos($xmlType,':')) !==false ? substr($xmlType,$dppos + 1) : $xmlType; // strip namespace, if any + $arrayType = substr($arrayType,0,-5); // strip 'Array' from name + if ($this->operationBodyStyle['use'] == self::USE_ENCODED) + { + $complexContent=$dom->createElement('xsd:complexContent'); + $restriction=$dom->createElement('xsd:restriction'); + $restriction->setAttribute('base','soap-enc:Array'); + $attribute=$dom->createElement('xsd:attribute'); + $attribute->setAttribute('ref','soap-enc:arrayType'); + $attribute->setAttribute('arrayType',(isset(self::$typeMap[$arrayType]) ? 'xsd:' : 'tns:') .$arrayType.'[]'); + + $restriction->appendChild($attribute); + $complexContent->appendChild($restriction); + $complexType->appendChild($complexContent); + } + else + { + $sequence=$dom->createElement('xsd:sequence'); + $element=$dom->createElement('xsd:element'); + $element->setAttribute('name','item'); + $element->setAttribute('type',(isset(self::$typeMap[$arrayType]) ? self::$typeMap[$arrayType] : 'tns:'.$arrayType)); + $element->setAttribute('minOccurs','0'); + $element->setAttribute('maxOccurs','unbounded'); + $sequence->appendChild($element); + $complexType->appendChild($sequence); + } } elseif(is_array($xmlType)) { @@ -503,8 +608,32 @@ protected function addTypes($dom) } } $schema->appendChild($complexType); - $types->appendChild($schema); } + foreach($this->elements as $name=>$parameters) + { + $element=$dom->createElement('xsd:element'); + $element->setAttribute('name',$name); + $complexType=$dom->createElement('xsd:complexType'); + if (!empty($parameters)) + { + $sequence=$dom->createElement('xsd:sequence'); + foreach($parameters as $paramName=>$paramOpts) + { + $innerElement=$dom->createElement('xsd:element'); + $innerElement->setAttribute('name',$paramName); + $innerElement->setAttribute('type',$paramOpts['type']); + if (isset($paramOpts['nillable']) && $paramOpts['nillable']) + { + $innerElement->setAttribute('nillable','true'); + } + $sequence->appendChild($innerElement); + } + $complexType->appendChild($sequence); + } + $element->appendChild($complexType); + $schema->appendChild($element); + } + $types->appendChild($schema); $dom->documentElement->appendChild($types); } @@ -523,7 +652,14 @@ protected function addMessages($dom) { $partElement=$dom->createElement('wsdl:part'); $partElement->setAttribute('name',$partName); - $partElement->setAttribute('type',$part[0]); + if (isset($part['type'])) + { + $partElement->setAttribute('type',$part['type']); + } + if (isset($part['element'])) + { + $partElement->setAttribute('element',$part['element']); + } $element->appendChild($partElement); } } @@ -539,8 +675,8 @@ protected function addPortTypes($dom) $portType=$dom->createElement('wsdl:portType'); $portType->setAttribute('name',$this->serviceName.'PortType'); $dom->documentElement->appendChild($portType); - foreach($this->operations as $name=>$doc) - $portType->appendChild($this->createPortElement($dom,$name,$doc)); + foreach($this->operations as $name=>$operation) + $portType->appendChild($this->createPortElement($dom,$name,$operation['doc'])); } /** @@ -554,9 +690,9 @@ protected function createPortElement($dom,$name,$doc) $operation->setAttribute('name',$name); $input=$dom->createElement('wsdl:input'); - $input->setAttribute('message', 'tns:'.$name.'Request'); + $input->setAttribute('message', 'tns:'.$name.'In'); $output=$dom->createElement('wsdl:output'); - $output->setAttribute('message', 'tns:'.$name.'Response'); + $output->setAttribute('message', 'tns:'.$name.'Out'); $operation->appendChild($dom->createElement('wsdl:documentation',$doc)); $operation->appendChild($input); @@ -575,37 +711,70 @@ protected function addBindings($dom) $binding->setAttribute('type','tns:'.$this->serviceName.'PortType'); $soapBinding=$dom->createElement('soap:binding'); - $soapBinding->setAttribute('style','rpc'); - $soapBinding->setAttribute('transport','http://schemas.xmlsoap.org/soap/http'); + $soapBinding->setAttribute('style',$this->bindingStyle); + $soapBinding->setAttribute('transport',$this->bindingTransport); $binding->appendChild($soapBinding); $dom->documentElement->appendChild($binding); - foreach($this->operations as $name=>$doc) - $binding->appendChild($this->createOperationElement($dom,$name)); + foreach($this->operations as $name=>$operation) + $binding->appendChild($this->createOperationElement($dom,$name,$operation['headers'])); } /** * @param DOMDocument $dom Represents an entire HTML or XML document; serves as the root of the document tree * @param string $name method name + * @param array $headers array like array('input'=>array(MESSAGE,PART),'output=>array(MESSAGE,PART)) */ - protected function createOperationElement($dom,$name) + protected function createOperationElement($dom,$name,$headers=null) { $operation=$dom->createElement('wsdl:operation'); $operation->setAttribute('name', $name); $soapOperation=$dom->createElement('soap:operation'); $soapOperation->setAttribute('soapAction', $this->namespace.'#'.$name); - $soapOperation->setAttribute('style','rpc'); + if ($this->bindingStyle == self::STYLE_RPC) + { + $soapOperation->setAttribute('style', self::STYLE_RPC); + } $input=$dom->createElement('wsdl:input'); $output=$dom->createElement('wsdl:output'); $soapBody=$dom->createElement('soap:body'); - $soapBody->setAttribute('use', 'encoded'); - $soapBody->setAttribute('namespace', $this->namespace); - $soapBody->setAttribute('encodingStyle', 'http://schemas.xmlsoap.org/soap/encoding/'); + $operationBodyStyle=$this->operationBodyStyle; + if ($this->bindingStyle == self::STYLE_RPC && !isset($operationBodyStyle['namespace'])) + { + $operationBodyStyle['namespace'] = $this->namespace; + } + foreach($operationBodyStyle as $attributeName=>$attributeValue) + { + $soapBody->setAttribute($attributeName, $attributeValue); + } $input->appendChild($soapBody); $output->appendChild(clone $soapBody); + if (is_array($headers)) + { + if (isset($headers['input']) && is_array($headers['input']) && count($headers['input'])==2) + { + $soapHeader = $dom->createElement('soap:header'); + foreach($operationBodyStyle as $attributeName=>$attributeValue) { + $soapHeader->setAttribute($attributeName, $attributeValue); + } + $soapHeader->setAttribute('message', $headers['input'][0]); + $soapHeader->setAttribute('part', $headers['input'][1]); + $input->appendChild($soapHeader); + } + if (isset($headers['output']) && is_array($headers['output']) && count($headers['output'])==2) + { + $soapHeader = $dom->createElement('soap:header'); + foreach($operationBodyStyle as $attributeName=>$attributeValue) { + $soapHeader->setAttribute($attributeName, $attributeValue); + } + $soapHeader->setAttribute('message', $headers['output'][0]); + $soapHeader->setAttribute('part', $headers['output'][1]); + $output->appendChild($soapHeader); + } + } $operation->appendChild($soapOperation); $operation->appendChild($input); @@ -648,7 +817,7 @@ protected function addService($dom,$serviceUrl) *
  • Max - maximum number of occurrences
  • *
  • Description - Detailed description of the attribute.
  • *
  • Example - Attribute example value if provided via PHPDoc property @example.
  • - *
      + *
    * * @param bool $return If true, generated HTML output will be returned rather than directly sent to output buffer */ diff --git a/framework/web/widgets/CActiveForm.php b/framework/web/widgets/CActiveForm.php index 1605fd43489..339e4369360 100644 --- a/framework/web/widgets/CActiveForm.php +++ b/framework/web/widgets/CActiveForm.php @@ -161,7 +161,7 @@ class CActiveForm extends CWidget */ public $stateful=false; /** - * @var string the CSS class name for error messages. + * @var string the CSS class name for error messages. * Since 1.1.14 this defaults to 'errorMessage' defined in {@link CHtml::$errorMessageCss}. * Individual {@link error} call may override this value by specifying the 'class' HTML option. */ @@ -331,7 +331,7 @@ public function init() echo CHtml::statefulForm($this->action, $this->method, $this->htmlOptions); else echo CHtml::beginForm($this->action, $this->method, $this->htmlOptions); - + if($this->errorMessageCssClass===null) $this->errorMessageCssClass=CHtml::$errorMessageCss; } @@ -410,9 +410,9 @@ public function run() *
      *
    • inputID
    • *
    - * When an CActiveForm input field uses a custom ID, for ajax/client validation to work properly + * When an CActiveForm input field uses a custom ID, for ajax/client validation to work properly * inputID should be set to the same ID - * + * * Example: *
     	 * 
    @@ -421,7 +421,7 @@ public function run() * error($model,'attribute',array('inputID'=>'custom-id')); ?> *
    *
    - * + * * When client-side validation is enabled, an option named "clientValidation" is also recognized. * This option should take a piece of JavaScript code to perform client-side validation. In the code, * the variables are predefined: @@ -515,7 +515,10 @@ public function error($model,$attribute,$htmlOptions=array(),$enableAjaxValidati $option['clientValidation']=new CJavaScriptExpression("function(value, messages, attribute) {\n".implode("\n",$validators)."\n}"); } - $html=CHtml::error($model,$attribute,$htmlOptions); + if(empty($option['hideErrorMessage']) && empty($this->clientOptions['hideErrorMessage'])) + $html=CHtml::error($model,$attribute,$htmlOptions); + else + $html=''; if($html==='') { if(isset($htmlOptions['style'])) @@ -695,9 +698,73 @@ public function timeField($model,$attribute,$htmlOptions=array()) } /** - * Renders a time field for a model attribute. - * This method is a wrapper of {@link CHtml::activeTimeField}. - * Please check {@link CHtml::activeTimeField} for detailed information + * Renders a datetime field for a model attribute. + * This method is a wrapper of {@link CHtml::activeDateTimeField}. + * Please check {@link CHtml::activeDateTimeField} for detailed information + * about the parameters for this method. + * @param CModel $model the data model + * @param string $attribute the attribute + * @param array $htmlOptions additional HTML attributes. + * @return string the generated input field + * @since 1.1.16 + */ + public function dateTimeField($model,$attribute,$htmlOptions=array()) + { + return CHtml::activeDateTimeField($model,$attribute,$htmlOptions); + } + + /** + * Renders a local datetime field for a model attribute. + * This method is a wrapper of {@link CHtml::activeDateTimeLocalField}. + * Please check {@link CHtml::activeDateTimeLocalField} for detailed information + * about the parameters for this method. + * @param CModel $model the data model + * @param string $attribute the attribute + * @param array $htmlOptions additional HTML attributes. + * @return string the generated input field + * @since 1.1.16 + */ + public function dateTimeLocalField($model,$attribute,$htmlOptions=array()) + { + return CHtml::activeDateTimeLocalField($model,$attribute,$htmlOptions); + } + + /** + * Renders a week field for a model attribute. + * This method is a wrapper of {@link CHtml::activeWeekField}. + * Please check {@link CHtml::activeWeekField} for detailed information + * about the parameters for this method. + * @param CModel $model the data model + * @param string $attribute the attribute + * @param array $htmlOptions additional HTML attributes. + * @return string the generated input field + * @since 1.1.16 + */ + public function weekField($model,$attribute,$htmlOptions=array()) + { + return CHtml::activeWeekField($model,$attribute,$htmlOptions); + } + + /** + * Renders a color picker field for a model attribute. + * This method is a wrapper of {@link CHtml::activeColorField}. + * Please check {@link CHtml::activeColorField} for detailed information + * about the parameters for this method. + * @param CModel $model the data model + * @param string $attribute the attribute + * @param array $htmlOptions additional HTML attributes. + * @return string the generated input field + * @since 1.1.16 + */ + public function colorField($model,$attribute,$htmlOptions=array()) + { + return CHtml::activeColorField($model,$attribute,$htmlOptions); + } + + /** + * Renders a tel field for a model attribute. + * This method is a wrapper of {@link CHtml::activeTelField}. + * Please check {@link CHtml::activeTelField} for detailed information * about the parameters for this method. * @param CModel $model the data model * @param string $attribute the attribute diff --git a/framework/web/widgets/CHtmlPurifier.php b/framework/web/widgets/CHtmlPurifier.php index f893c30ac46..23d33d3602f 100644 --- a/framework/web/widgets/CHtmlPurifier.php +++ b/framework/web/widgets/CHtmlPurifier.php @@ -88,7 +88,7 @@ public function purify($content) /** * Set the options for HTML Purifier and create a new HTML Purifier instance based on these options. * @param mixed $options the options for HTML Purifier - * @return CHtmlPurifier + * @return static the object instance itself */ public function setOptions($options) { diff --git a/framework/web/widgets/captcha/CCaptcha.php b/framework/web/widgets/captcha/CCaptcha.php index c19c48827c6..5570203c577 100644 --- a/framework/web/widgets/captcha/CCaptcha.php +++ b/framework/web/widgets/captcha/CCaptcha.php @@ -27,6 +27,9 @@ * A {@link CCaptchaValidator} may be used to validate that the user enters * a verification code matching the code displayed in the CAPTCHA image. * + * When combining CCaptcha with CActiveForm or CForm, make sure ajaxValidation is disabled. Performing ajax validation causes + * your Captcha to be refreshed, rendering the code invalid on the next validation attempt. + * * @author Qiang Xue * @package system.web.widgets.captcha * @since 1.0 diff --git a/framework/web/widgets/captcha/CCaptchaAction.php b/framework/web/widgets/captcha/CCaptchaAction.php index 94438317554..5a2f586ce32 100644 --- a/framework/web/widgets/captcha/CCaptchaAction.php +++ b/framework/web/widgets/captcha/CCaptchaAction.php @@ -258,7 +258,7 @@ protected function renderImageGD($code) $this->foreColor % 0x100); if($this->fontFile === null) - $this->fontFile = dirname(__FILE__) . '/SpicyRice.ttf'; + $this->fontFile = dirname(__FILE__).DIRECTORY_SEPARATOR.'SpicyRice.ttf'; $length = strlen($code); $box = imagettfbbox(30,0,$this->fontFile,$code); @@ -301,7 +301,7 @@ protected function renderImageImagick($code) $image->newImage($this->width,$this->height,$backColor); if($this->fontFile===null) - $this->fontFile=dirname(__FILE__).'/SpicyRice.ttf'; + $this->fontFile=dirname(__FILE__).DIRECTORY_SEPARATOR.'SpicyRice.ttf'; $draw=new ImagickDraw(); $draw->setFont($this->fontFile); @@ -331,6 +331,6 @@ protected function renderImageImagick($code) header('Content-Transfer-Encoding: binary'); header("Content-Type: image/png"); $image->setImageFormat('png'); - echo $image; + echo $image->getImageBlob(); } -} \ No newline at end of file +} diff --git a/framework/web/widgets/pagers/CLinkPager.php b/framework/web/widgets/pagers/CLinkPager.php index 26751b8bce4..d5ed1299edb 100644 --- a/framework/web/widgets/pagers/CLinkPager.php +++ b/framework/web/widgets/pagers/CLinkPager.php @@ -66,18 +66,22 @@ class CLinkPager extends CBasePager public $maxButtonCount=10; /** * @var string the text label for the next page button. Defaults to 'Next >'. + * Setting this to false will disable this button. */ public $nextPageLabel; /** * @var string the text label for the previous page button. Defaults to '< Previous'. + * Setting this to false will disable this button. */ public $prevPageLabel; /** * @var string the text label for the first page button. Defaults to '<< First'. + * Setting this to false will disable this button. */ public $firstPageLabel; /** * @var string the text label for the last page button. Defaults to 'Last >>'. + * Setting this to false will disable this button. */ public $lastPageLabel; /** @@ -149,26 +153,32 @@ protected function createPageButtons() list($beginPage,$endPage)=$this->getPageRange(); $currentPage=$this->getCurrentPage(false); // currentPage is calculated in getPageRange() $buttons=array(); - + // first page - $buttons[]=$this->createPageButton($this->firstPageLabel,0,$this->firstPageCssClass,$currentPage<=0,false); - + if ($this->firstPageLabel !== false) { + $buttons[]=$this->createPageButton($this->firstPageLabel,0,$this->firstPageCssClass,$currentPage<=0,false); + } // prev page - if(($page=$currentPage-1)<0) - $page=0; - $buttons[]=$this->createPageButton($this->prevPageLabel,$page,$this->previousPageCssClass,$currentPage<=0,false); + if ($this->prevPageLabel !== false) { + if(($page=$currentPage-1)<0) + $page=0; + $buttons[]=$this->createPageButton($this->prevPageLabel,$page,$this->previousPageCssClass,$currentPage<=0,false); + } // internal pages for($i=$beginPage;$i<=$endPage;++$i) $buttons[]=$this->createPageButton($i+1,$i,$this->internalPageCssClass,false,$i==$currentPage); - + // next page - if(($page=$currentPage+1)>=$pageCount-1) - $page=$pageCount-1; - $buttons[]=$this->createPageButton($this->nextPageLabel,$page,$this->nextPageCssClass,$currentPage>=$pageCount-1,false); - + if ($this->nextPageLabel !== false) { + if(($page=$currentPage+1)>=$pageCount-1) + $page=$pageCount-1; + $buttons[]=$this->createPageButton($this->nextPageLabel,$page,$this->nextPageCssClass,$currentPage>=$pageCount-1,false); + } // last page - $buttons[]=$this->createPageButton($this->lastPageLabel,$pageCount-1,$this->lastPageCssClass,$currentPage>=$pageCount-1,false); + if ($this->lastPageLabel !== false) { + $buttons[]=$this->createPageButton($this->lastPageLabel,$pageCount-1,$this->lastPageCssClass,$currentPage>=$pageCount-1,false); + } return $buttons; } diff --git a/framework/yiic b/framework/yiic old mode 100644 new mode 100755 diff --git a/framework/yiilite.php b/framework/yiilite.php index fc9ce51e4ec..553d571398f 100644 --- a/framework/yiilite.php +++ b/framework/yiilite.php @@ -40,7 +40,7 @@ class YiiBase private static $_logger; public static function getVersion() { - return '1.1.15'; + return '1.1.16'; } public static function createWebApplication($config=null) { @@ -145,7 +145,8 @@ public static function import($alias,$forceInclude=false) } if(($pos=strrpos($alias,'.'))===false) // a simple class name { - if($forceInclude && self::autoload($alias)) + // try to autoload the class with an autoloader if $forceInclude is true + if($forceInclude && (Yii::autoload($alias,true) || class_exists($alias,true))) self::$_imports[$alias]=$alias; return $alias; } @@ -211,13 +212,15 @@ public static function setPathOfAlias($alias,$path) else self::$_aliases[$alias]=rtrim($path,'\\/'); } - public static function autoload($className) + public static function autoload($className,$classMapOnly=false) { // use include so that the error PHP file may appear if(isset(self::$classMap[$className])) include(self::$classMap[$className]); elseif(isset(self::$_coreClasses[$className])) include(YII_PATH.self::$_coreClasses[$className]); + elseif($classMapOnly) + return false; else { // include class file relying on include_path @@ -420,6 +423,9 @@ public static function registerAutoloader($callback, $append=false) 'CDbExpression' => '/db/schema/CDbExpression.php', 'CDbSchema' => '/db/schema/CDbSchema.php', 'CDbTableSchema' => '/db/schema/CDbTableSchema.php', + 'CCubridColumnSchema' => '/db/schema/cubrid/CCubridColumnSchema.php', + 'CCubridSchema' => '/db/schema/cubrid/CCubridSchema.php', + 'CCubridTableSchema' => '/db/schema/cubrid/CCubridTableSchema.php', 'CMssqlColumnSchema' => '/db/schema/mssql/CMssqlColumnSchema.php', 'CMssqlCommandBuilder' => '/db/schema/mssql/CMssqlCommandBuilder.php', 'CMssqlPdoAdapter' => '/db/schema/mssql/CMssqlPdoAdapter.php', @@ -461,6 +467,7 @@ public static function registerAutoloader($callback, $append=false) 'CLogRouter' => '/logging/CLogRouter.php', 'CLogger' => '/logging/CLogger.php', 'CProfileLogRoute' => '/logging/CProfileLogRoute.php', + 'CSysLogRoute' => '/logging/CSysLogRoute.php', 'CWebLogRoute' => '/logging/CWebLogRoute.php', 'CDateTimeParser' => '/utils/CDateTimeParser.php', 'CFileHelper' => '/utils/CFileHelper.php', @@ -699,7 +706,7 @@ public function __call($name,$parameters) return call_user_func_array(array($object,$name),$parameters); } } - if(class_exists('Closure', false) && $this->canGetProperty($name) && $this->$name instanceof Closure) + if(class_exists('Closure', false) && ($this->canGetProperty($name) || property_exists($this, $name)) && $this->$name instanceof Closure) return call_user_func_array($this->$name, $parameters); throw new CException(Yii::t('yii','{class} and its behaviors do not have a method or closure named "{name}".', array('{class}'=>get_class($this), '{name}'=>$name))); @@ -1022,7 +1029,7 @@ public function getModules() { return $this->_moduleConfig; } - public function setModules($modules) + public function setModules($modules,$merge=true) { foreach($modules as $id=>$module) { @@ -1031,15 +1038,18 @@ public function setModules($modules) $id=$module; $module=array(); } - if(!isset($module['class'])) - { - Yii::setPathOfAlias($id,$this->getModulePath().DIRECTORY_SEPARATOR.$id); - $module['class']=$id.'.'.ucfirst($id).'Module'; - } - if(isset($this->_moduleConfig[$id])) + if(isset($this->_moduleConfig[$id]) && $merge) $this->_moduleConfig[$id]=CMap::mergeArray($this->_moduleConfig[$id],$module); else + { + if(!isset($module['class'])) + { + if (Yii::getPathOfAlias($id)===false) + Yii::setPathOfAlias($id,$this->getModulePath().DIRECTORY_SEPARATOR.$id); + $module['class']=$id.'.'.ucfirst($id).'Module'; + } $this->_moduleConfig[$id]=$module; + } } } public function hasComponent($id) @@ -1138,6 +1148,7 @@ abstract class CApplication extends CModule public $name='My Application'; public $charset='UTF-8'; public $sourceLanguage='en_us'; + public $localeClass='CLocale'; private $_id; private $_basePath; private $_runtimePath; @@ -1289,15 +1300,19 @@ public function findLocalizedFile($srcFile,$srcLanguage=null,$language=null) } public function getLocale($localeID=null) { - return CLocale::getInstance($localeID===null?$this->getLanguage():$localeID); + return call_user_func_array(array($this->localeClass, 'getInstance'),array($localeID===null?$this->getLanguage():$localeID)); } public function getLocaleDataPath() { - return CLocale::$dataPath===null ? Yii::getPathOfAlias('system.i18n.data') : CLocale::$dataPath; + $vars=get_class_vars($this->localeClass); + if(empty($vars['dataPath'])) + return Yii::getPathOfAlias('system.i18n.data'); + return $vars['dataPath']; } public function setLocaleDataPath($value) { - CLocale::$dataPath=$value; + $property=new ReflectionProperty($this->localeClass,'dataPath'); + $property->setValue($value); } public function getNumberFormatter() { @@ -1785,7 +1800,7 @@ public function createController($route,$owner=null) $className=ucfirst($id).'Controller'; $classFile=$basePath.DIRECTORY_SEPARATOR.$className.'.php'; if($owner->controllerNamespace!==null) - $className=$owner->controllerNamespace.'\\'.$className; + $className=$owner->controllerNamespace.'\\'.str_replace('/','\\',$controllerID).$className; if(is_file($classFile)) { if(!class_exists($className,false)) @@ -2262,6 +2277,7 @@ class CHttpRequest extends CApplicationComponent private $_preferredLanguages; private $_csrfToken; private $_restParams; + private $_httpVersion; public function init() { parent::init(); @@ -2333,6 +2349,18 @@ public function getPut($name,$defaultValue=null) else return $defaultValue; } + public function getPatch($name,$defaultValue=null) + { + if($this->getIsPatchViaPostRequest()) + return $this->getPost($name, $defaultValue); + if($this->getIsPatchRequest()) + { + $restParams=$this->getRestParams(); + return isset($restParams[$name]) ? $restParams[$name] : $defaultValue; + } + else + return $defaultValue; + } public function getRestParams() { if($this->_restParams===null) @@ -2447,7 +2475,13 @@ public function getPathInfo() $pathInfo=substr($_SERVER['PHP_SELF'],strlen($scriptUrl)); else throw new CException(Yii::t('yii','CHttpRequest is unable to determine the path info of the request.')); - $this->_pathInfo=trim($pathInfo,'/'); + if($pathInfo==='/') + $pathInfo=''; + elseif($pathInfo[0]==='/') + $pathInfo=substr($pathInfo,1); + if(($posEnd=strlen($pathInfo)-1)>0 && $pathInfo[$posEnd]==='/') + $pathInfo=substr($pathInfo,0,$posEnd); + $this->_pathInfo=$pathInfo; } return $this->_pathInfo; } @@ -2508,13 +2542,15 @@ public function getQueryString() } public function getIsSecureConnection() { - return isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']=='on' || $_SERVER['HTTPS']==1) - || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO']=='https'; + return isset($_SERVER['HTTPS']) && (strcasecmp($_SERVER['HTTPS'],'on')===0 || $_SERVER['HTTPS']==1) + || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strcasecmp($_SERVER['HTTP_X_FORWARDED_PROTO'],'https')===0; } public function getRequestType() { if(isset($_POST['_method'])) return strtoupper($_POST['_method']); + elseif(isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) + return strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']); return strtoupper(isset($_SERVER['REQUEST_METHOD'])?$_SERVER['REQUEST_METHOD']:'GET'); } public function getIsPostRequest() @@ -2537,6 +2573,14 @@ protected function getIsPutViaPostRequest() { return isset($_POST['_method']) && !strcasecmp($_POST['_method'],'PUT'); } + public function getIsPatchRequest() + { + return (isset($_SERVER['REQUEST_METHOD']) && !strcasecmp($_SERVER['REQUEST_METHOD'],'PATCH')) || $this->getIsPatchViaPostRequest(); + } + protected function getIsPatchViaPostRequest() + { + return isset($_POST['_method']) && !strcasecmp($_POST['_method'],'PATCH'); + } public function getIsAjaxRequest() { return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest'; @@ -2736,10 +2780,23 @@ public function getPreferredLanguages() } return $this->_preferredLanguages; } - public function getPreferredLanguage() + public function getPreferredLanguage($languages=array()) { $preferredLanguages=$this->getPreferredLanguages(); - return !empty($preferredLanguages) ? CLocale::getCanonicalID($preferredLanguages[0]) : false; + if(empty($languages)) { + return !empty($preferredLanguages) ? CLocale::getCanonicalID($preferredLanguages[0]) : false; + } + foreach ($preferredLanguages as $preferredLanguage) { + $preferredLanguage=CLocale::getCanonicalID($preferredLanguage); + foreach ($languages as $language) { + $language=CLocale::getCanonicalID($language); + // en_us==en_us, en==en_us, en_us==en + if($language===$acceptedLanguage || strpos($acceptedLanguage,$language.'_')===0 || strpos($language,$acceptedLanguage.'_')===0) { + return $language; + } + } + } + return reset($languages); } public function sendFile($fileName,$content,$mimeType=null,$terminate=true) { @@ -2751,6 +2808,7 @@ public function sendFile($fileName,$content,$mimeType=null,$terminate=true) $fileSize=(function_exists('mb_strlen') ? mb_strlen($content,'8bit') : strlen($content)); $contentStart=0; $contentEnd=$fileSize-1; + $httpVersion=$this->getHttpVersion(); if(isset($_SERVER['HTTP_RANGE'])) { header('Accept-Ranges: bytes'); @@ -2784,11 +2842,11 @@ public function sendFile($fileName,$content,$mimeType=null,$terminate=true) header("Content-Range: bytes $contentStart-$contentEnd/$fileSize"); throw new CHttpException(416,'Requested Range Not Satisfiable'); } - header('HTTP/1.1 206 Partial Content'); + header("HTTP/$httpVersion 206 Partial Content"); header("Content-Range: bytes $contentStart-$contentEnd/$fileSize"); } else - header('HTTP/1.1 200 OK'); + header("HTTP/$httpVersion 200 OK"); $length=$contentEnd-$contentStart+1; // Calculate new content length header('Pragma: public'); header('Expires: 0'); @@ -2866,6 +2924,7 @@ public function validateCsrfToken($event) { if ($this->getIsPostRequest() || $this->getIsPutRequest() || + $this->getIsPatchRequest() || $this->getIsDeleteRequest()) { $cookies=$this->getCookies(); @@ -2878,6 +2937,9 @@ public function validateCsrfToken($event) case 'PUT': $userToken=$this->getPut($this->csrfTokenName); break; + case 'PATCH': + $userToken=$this->getPatch($this->csrfTokenName); + break; case 'DELETE': $userToken=$this->getDelete($this->csrfTokenName); } @@ -2892,6 +2954,17 @@ public function validateCsrfToken($event) throw new CHttpException(400,Yii::t('yii','The CSRF token could not be verified.')); } } + public function getHttpVersion() + { + if($this->_httpVersion===null) + { + if(isset($_SERVER['SERVER_PROTOCOL']) && $_SERVER['SERVER_PROTOCOL']==='HTTP/1.0') + $this->_httpVersion='1.0'; + else + $this->_httpVersion='1.1'; + } + return $this->_httpVersion; + } } class CCookieCollection extends CMap { @@ -3237,6 +3310,7 @@ public function __construct($route,$pattern) } $this->route=trim($route,'/'); $tr2['/']=$tr['/']='\\/'; + $tr['.']='\\.'; if(strpos($route,'<')!==false && preg_match_all('/<(\w+)>/',$route,$matches2)) { foreach($matches2[1] as $name) @@ -3434,8 +3508,16 @@ public function widget($className,$properties=array(),$captureOutput=false) { ob_start(); ob_implicit_flush(false); - $widget=$this->createWidget($className,$properties); - $widget->run(); + try + { + $widget=$this->createWidget($className,$properties); + $widget->run(); + } + catch(Exception $e) + { + ob_end_clean(); + throw $e; + } return ob_get_clean(); } else @@ -4013,8 +4095,8 @@ public function runWithParams($params) $method=new ReflectionMethod($this, 'run'); if($method->getNumberOfParameters()>0) return $this->runWithParamsInternal($this, $method, $params); - else - return $this->run(); + $this->run(); + return true; } protected function runWithParamsInternal($object, $method, $params) { @@ -4054,8 +4136,8 @@ public function runWithParams($params) $method=new ReflectionMethod($controller, $methodName); if($method->getNumberOfParameters()>0) return $this->runWithParamsInternal($controller, $method, $params); - else - return $controller->$methodName(); + $controller->$methodName(); + return true; } } class CWebUser extends CApplicationComponent implements IWebUser @@ -4502,7 +4584,8 @@ public function setSessionID($value) } public function regenerateID($deleteOldSession=false) { - session_regenerate_id($deleteOldSession); + if($this->getIsStarted()) + session_regenerate_id($deleteOldSession); } public function getSessionName() { @@ -4813,6 +4896,13 @@ public static function form($action='',$method='post',$htmlOptions=array()) public static function beginForm($action='',$method='post',$htmlOptions=array()) { $htmlOptions['action']=$url=self::normalizeUrl($action); + if(strcasecmp($method,'get')!==0 && strcasecmp($method,'post')!==0) + { + $customMethod=$method; + $method='post'; + } + else + $customMethod=false; $htmlOptions['method']=$method; $form=self::tag('form',$htmlOptions,false,false); $hiddens=array(); @@ -4829,8 +4919,10 @@ public static function beginForm($action='',$method='post',$htmlOptions=array()) $request=Yii::app()->request; if($request->enableCsrfValidation && !strcasecmp($method,'post')) $hiddens[]=self::hiddenField($request->csrfTokenName,$request->getCsrfToken(),array('id'=>false)); + if($customMethod!==false) + $hiddens[]=self::hiddenField('_method',$customMethod); if($hiddens!==array()) - $form.="\n".self::tag('div',array('style'=>'display:none'),implode("\n",$hiddens)); + $form.="\n".implode("\n",$hiddens); return $form; } public static function endForm() @@ -4930,11 +5022,21 @@ public static function label($label,$for,$htmlOptions=array()) } return self::tag('label',$htmlOptions,$label); } + public static function colorField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('color',$name,$value,$htmlOptions); + } public static function textField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); return self::inputField('text',$name,$value,$htmlOptions); } + public static function searchField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('search',$name,$value,$htmlOptions); + } public static function numberField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); @@ -4955,6 +5057,21 @@ public static function timeField($name,$value='',$htmlOptions=array()) self::clientChange('change',$htmlOptions); return self::inputField('time',$name,$value,$htmlOptions); } + public static function dateTimeField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('datetime',$name,$value,$htmlOptions); + } + public static function dateTimeLocalField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('datetime-local',$name,$value,$htmlOptions); + } + public static function weekField($name,$value='',$htmlOptions=array()) + { + self::clientChange('change',$htmlOptions); + return self::inputField('week',$name,$value,$htmlOptions); + } public static function emailField($name,$value='',$htmlOptions=array()) { self::clientChange('change',$htmlOptions); @@ -5089,7 +5206,7 @@ public static function listBox($name,$select,$data,$htmlOptions=array()) public static function checkBoxList($name,$select,$data,$htmlOptions=array()) { $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}'; - $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:"
    \n"; + $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:self::tag('br'); $container=isset($htmlOptions['container'])?$htmlOptions['container']:'span'; unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']); if(substr($name,-2)!=='[]') @@ -5166,7 +5283,7 @@ public static function checkBoxList($name,$select,$data,$htmlOptions=array()) public static function radioButtonList($name,$select,$data,$htmlOptions=array()) { $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}'; - $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:"
    \n"; + $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:self::tag('br'); $container=isset($htmlOptions['container'])?$htmlOptions['container']:'span'; unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']); $labelOptions=isset($htmlOptions['labelOptions'])?$htmlOptions['labelOptions']:array(); @@ -5175,7 +5292,7 @@ public static function radioButtonList($name,$select,$data,$htmlOptions=array()) { if(!is_array($htmlOptions['empty'])) $htmlOptions['empty']=array(''=>$htmlOptions['empty']); - $data=array_merge($htmlOptions['empty'],$data); + $data=CMap::mergeArray($htmlOptions['empty'],$data); unset($htmlOptions['empty']); } $items=array(); @@ -5363,6 +5480,30 @@ public static function activeTimeField($model,$attribute,$htmlOptions=array()) self::clientChange('change',$htmlOptions); return self::activeInputField('time',$model,$attribute,$htmlOptions); } + public static function activeDateTimeField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + return self::activeInputField('datetime',$model,$attribute,$htmlOptions); + } + public static function activeDateTimeLocalField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + return self::activeInputField('datetime-local',$model,$attribute,$htmlOptions); + } + public static function activeWeekField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + return self::activeInputField('week',$model,$attribute,$htmlOptions); + } + public static function activeColorField($model,$attribute,$htmlOptions=array()) + { + self::resolveNameID($model,$attribute,$htmlOptions); + self::clientChange('change',$htmlOptions); + return self::activeInputField('color',$model,$attribute,$htmlOptions); + } public static function activeTelField($model,$attribute,$htmlOptions=array()) { self::resolveNameID($model,$attribute,$htmlOptions); @@ -5633,7 +5774,9 @@ public static function activeName($model,$attribute) protected static function activeInputField($type,$model,$attribute,$htmlOptions) { $htmlOptions['type']=$type; - if($type==='text' || $type==='password') + if($type==='text'||$type==='password'||$type==='color'||$type==='date'||$type==='datetime'|| + $type==='datetime-local'||$type==='email'||$type==='month'||$type==='number'||$type==='range'|| + $type==='search'||$type==='tel'||$type==='time'||$type==='url'||$type==='week') { if(!isset($htmlOptions['maxlength'])) { @@ -5844,9 +5987,9 @@ protected static function addErrorCss(&$htmlOptions) public static function renderAttributes($htmlOptions) { static $specialAttributes=array( - 'async'=>1, 'autofocus'=>1, 'autoplay'=>1, + 'async'=>1, 'checked'=>1, 'controls'=>1, 'declare'=>1, @@ -5856,6 +5999,7 @@ public static function renderAttributes($htmlOptions) 'formnovalidate'=>1, 'hidden'=>1, 'ismap'=>1, + 'itemscope'=>1, 'loop'=>1, 'multiple'=>1, 'muted'=>1, @@ -5885,7 +6029,10 @@ public static function renderAttributes($htmlOptions) { if(isset($specialAttributes[$name])) { - if($value) + if($value===false && $name==='async') { + $html .= ' ' . $name.'="false"'; + } + elseif($value) { $html .= ' ' . $name; if(self::$renderSpecialAttributesValue) @@ -6184,13 +6331,14 @@ protected function renderScriptBatch(array $scripts) $scriptContent = $scriptValue['content']; unset($scriptValue['content']); $scriptHtmlOptions = $scriptValue; + ksort($scriptHtmlOptions); } else { $scriptContent = $scriptValue; $scriptHtmlOptions = array(); } - $key=serialize(ksort($scriptHtmlOptions)); + $key=serialize($scriptHtmlOptions); $scriptBatches[$key]['htmlOptions']=$scriptHtmlOptions; $scriptBatches[$key]['scripts'][]=$scriptContent; } @@ -6395,6 +6543,10 @@ public function registerCoreScript($name) $params=func_get_args(); $this->recordCachingAction('clientScript','registerCoreScript',$params); } + elseif(YII_DEBUG) + throw new CException('There is no CClientScript package: '.$name); + else + Yii::log('There is no CClientScript package: '.$name,CLogger::LEVEL_WARNING,'system.web.CClientScript'); return $this; } public function registerCssFile($url,$media='') @@ -7401,7 +7553,10 @@ public function refreshMetaData() } public function tableName() { - return get_class($this); + $tableName = get_class($this); + if(($pos=strrpos($tableName,'\\')) !== false) + return substr($tableName,$pos+1); + return $tableName; } public function primaryKey() { @@ -7641,7 +7796,7 @@ public function insert($attributes=null) if($this->beforeSave()) { $builder=$this->getCommandBuilder(); - $table=$this->getMetaData()->tableSchema; + $table=$this->getTableSchema(); $command=$builder->createInsertCommand($table,$this->getAttributes($attributes)); if($command->execute()) { @@ -7767,7 +7922,7 @@ public function equals($record) } public function getPrimaryKey() { - $table=$this->getMetaData()->tableSchema; + $table=$this->getTableSchema(); if(is_string($table->primaryKey)) return $this->{$table->primaryKey}; elseif(is_array($table->primaryKey)) @@ -7783,7 +7938,7 @@ public function getPrimaryKey() public function setPrimaryKey($value) { $this->_pk=$this->getPrimaryKey(); - $table=$this->getMetaData()->tableSchema; + $table=$this->getTableSchema(); if(is_string($table->primaryKey)) $this->{$table->primaryKey}=$value; elseif(is_array($table->primaryKey)) @@ -7808,7 +7963,7 @@ protected function query($criteria,$all=false) { if(!$all) $criteria->limit=1; - $command=$this->getCommandBuilder()->createFindCommand($this->getTableSchema(),$criteria,$this->getTableAlias()); + $command=$this->getCommandBuilder()->createFindCommand($this->getTableSchema(),$criteria); return $all ? $this->populateRecords($command->queryAll(), true, $criteria->index) : $this->populateRecord($command->queryRow()); } else @@ -7936,8 +8091,8 @@ public function findAllBySql($sql,$params=array()) } public function count($condition='',$params=array()) { - $builder=$this->getCommandBuilder(); $this->beforeCount(); + $builder=$this->getCommandBuilder(); $criteria=$builder->createCriteria($condition,$params); $this->applyScopes($criteria); if(empty($criteria->with)) @@ -8106,6 +8261,7 @@ class CBaseActiveRelation extends CComponent public $params=array(); public $group=''; public $join=''; + public $joinOptions=''; public $having=''; public $order=''; public function __construct($name,$className,$foreignKey,$options=array()) @@ -8174,6 +8330,7 @@ class CStatRelation extends CBaseActiveRelation { public $select='COUNT(*)'; public $defaultValue=0; + public $scopes; public function mergeWith($criteria,$fromScope=false) { if($criteria instanceof CDbCriteria) @@ -8288,9 +8445,9 @@ public function __construct($model) if(($table=$model->getDbConnection()->getSchema()->getTable($tableName))===null) throw new CDbException(Yii::t('yii','The table "{table}" for active record class "{class}" cannot be found in the database.', array('{class}'=>$this->_modelClassName,'{table}'=>$tableName))); - if($table->primaryKey===null) + if(($modelPk=$model->primaryKey())!==null || $table->primaryKey===null) { - $table->primaryKey=$model->primaryKey(); + $table->primaryKey=$modelPk; if(is_string($table->primaryKey) && isset($table->columns[$table->primaryKey])) $table->columns[$table->primaryKey]->isPrimaryKey=true; elseif(is_array($table->primaryKey)) @@ -8350,9 +8507,10 @@ class CDbConnection extends CApplicationComponent public $tablePrefix; public $initSQLs; public $driverMap=array( + 'cubrid'=>'CCubridSchema', // CUBRID 'pgsql'=>'CPgsqlSchema', // PostgreSQL 'mysqli'=>'CMysqlSchema', // MySQL - 'mysql'=>'CMysqlSchema', // MySQL + 'mysql'=>'CMysqlSchema', // MySQL,MariaDB 'sqlite'=>'CSqliteSchema', // sqlite 3 'sqlite2'=>'CSqliteSchema', // sqlite 2 'mssql'=>'CMssqlSchema', // Mssql driver on windows hosts @@ -8361,6 +8519,7 @@ class CDbConnection extends CApplicationComponent 'oci'=>'COciSchema', // Oracle driver ); public $pdoClass = 'PDO'; + private $_driverName; private $_attributes=array(); private $_active=false; private $_pdo; @@ -8444,9 +8603,8 @@ protected function close() protected function createPdoInstance() { $pdoClass=$this->pdoClass; - if(($pos=strpos($this->connectionString,':'))!==false) + if(($driver=$this->getDriverName())!==null) { - $driver=strtolower(substr($this->connectionString,0,$pos)); if($driver==='mssql' || $driver==='dblib') $pdoClass='CMssqlPdoAdapter'; elseif($driver==='sqlsrv') @@ -8588,9 +8746,15 @@ public function setPersistent($value) } public function getDriverName() { - if(($pos=strpos($this->connectionString, ':'))!==false) - return strtolower(substr($this->connectionString, 0, $pos)); - // return $this->getAttribute(PDO::ATTR_DRIVER_NAME); + if($this->_driverName!==null) + return $this->_driverName; + elseif(($pos=strpos($this->connectionString,':'))!==false) + return $this->_driverName=strtolower(substr($this->connectionString,0,$pos)); + //return $this->getAttribute(PDO::ATTR_DRIVER_NAME); + } + public function setDriverName($driverName) + { + $this->_driverName=strtolower($driverName); } public function getClientVersion() { @@ -8815,7 +8979,7 @@ public function getColumnType($type) else return $type; } - public function createTable($table, $columns, $options=null) + public function createTable($table,$columns,$options=null) { $cols=array(); foreach($columns as $name=>$type) @@ -8828,7 +8992,7 @@ public function createTable($table, $columns, $options=null) $sql="CREATE TABLE ".$this->quoteTableName($table)." (\n".implode(",\n",$cols)."\n)"; return $options===null ? $sql : $sql.' '.$options; } - public function renameTable($table, $newName) + public function renameTable($table,$newName) { return 'RENAME TABLE ' . $this->quoteTableName($table) . ' TO ' . $this->quoteTableName($newName); } @@ -8840,58 +9004,61 @@ public function truncateTable($table) { return "TRUNCATE TABLE ".$this->quoteTableName($table); } - public function addColumn($table, $column, $type) + public function addColumn($table,$column,$type) { return 'ALTER TABLE ' . $this->quoteTableName($table) . ' ADD ' . $this->quoteColumnName($column) . ' ' . $this->getColumnType($type); } - public function dropColumn($table, $column) + public function dropColumn($table,$column) { return "ALTER TABLE ".$this->quoteTableName($table) ." DROP COLUMN ".$this->quoteColumnName($column); } - public function renameColumn($table, $name, $newName) + public function renameColumn($table,$name,$newName) { return "ALTER TABLE ".$this->quoteTableName($table) . " RENAME COLUMN ".$this->quoteColumnName($name) . " TO ".$this->quoteColumnName($newName); } - public function alterColumn($table, $column, $type) + public function alterColumn($table,$column,$type) { return 'ALTER TABLE ' . $this->quoteTableName($table) . ' CHANGE ' . $this->quoteColumnName($column) . ' ' . $this->quoteColumnName($column) . ' ' . $this->getColumnType($type); } - public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete=null, $update=null) + public function addForeignKey($name,$table,$columns,$refTable,$refColumns,$delete=null,$update=null) { - $columns=preg_split('/\s*,\s*/',$columns,-1,PREG_SPLIT_NO_EMPTY); + if(is_string($columns)) + $columns=preg_split('/\s*,\s*/',$columns,-1,PREG_SPLIT_NO_EMPTY); foreach($columns as $i=>$col) $columns[$i]=$this->quoteColumnName($col); - $refColumns=preg_split('/\s*,\s*/',$refColumns,-1,PREG_SPLIT_NO_EMPTY); + if(is_string($refColumns)) + $refColumns=preg_split('/\s*,\s*/',$refColumns,-1,PREG_SPLIT_NO_EMPTY); foreach($refColumns as $i=>$col) $refColumns[$i]=$this->quoteColumnName($col); $sql='ALTER TABLE '.$this->quoteTableName($table) .' ADD CONSTRAINT '.$this->quoteColumnName($name) - .' FOREIGN KEY ('.implode(', ', $columns).')' + .' FOREIGN KEY ('.implode(', ',$columns).')' .' REFERENCES '.$this->quoteTableName($refTable) - .' ('.implode(', ', $refColumns).')'; + .' ('.implode(', ',$refColumns).')'; if($delete!==null) $sql.=' ON DELETE '.$delete; if($update!==null) $sql.=' ON UPDATE '.$update; return $sql; } - public function dropForeignKey($name, $table) + public function dropForeignKey($name,$table) { return 'ALTER TABLE '.$this->quoteTableName($table) .' DROP CONSTRAINT '.$this->quoteColumnName($name); } - public function createIndex($name, $table, $column, $unique=false) + public function createIndex($name,$table,$columns,$unique=false) { $cols=array(); - $columns=preg_split('/\s*,\s*/',$column,-1,PREG_SPLIT_NO_EMPTY); + if(is_string($columns)) + $columns=preg_split('/\s*,\s*/',$columns,-1,PREG_SPLIT_NO_EMPTY); foreach($columns as $col) { if(strpos($col,'(')!==false) @@ -8903,7 +9070,7 @@ public function createIndex($name, $table, $column, $unique=false) . $this->quoteTableName($name).' ON ' . $this->quoteTableName($table).' ('.implode(', ',$cols).')'; } - public function dropIndex($name, $table) + public function dropIndex($name,$table) { return 'DROP INDEX '.$this->quoteTableName($name).' ON '.$this->quoteTableName($table); } @@ -8915,7 +9082,7 @@ public function addPrimaryKey($name,$table,$columns) $columns[$i]=$this->quoteColumnName($col); return 'ALTER TABLE ' . $this->quoteTableName($table) . ' ADD CONSTRAINT ' . $this->quoteColumnName($name) . ' PRIMARY KEY (' - . implode(', ', $columns). ' )'; + . implode(', ',$columns). ' )'; } public function dropPrimaryKey($name,$table) { @@ -8927,9 +9094,11 @@ class CSqliteSchema extends CDbSchema { public $columnTypes=array( 'pk' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', + 'bigpk' => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL', 'string' => 'varchar(255)', 'text' => 'text', 'integer' => 'integer', + 'bigint' => 'integer', 'float' => 'float', 'decimal' => 'decimal', 'datetime' => 'datetime', @@ -9297,7 +9466,7 @@ private function queryInternal($method,$mode,$params=array()) && ($cache=Yii::app()->getComponent($this->_connection->queryCacheID))!==null) { $this->_connection->queryCachingCount--; - $cacheKey='yii:dbquery'.$this->_connection->connectionString.':'.$this->_connection->username; + $cacheKey='yii:dbquery'.':'.$method.':'.$this->_connection->connectionString.':'.$this->_connection->username; $cacheKey.=':'.$this->getText().':'.serialize(array_merge($this->_paramLog,$params)); if(($result=$cache->get($cacheKey))!==false) { @@ -9348,8 +9517,6 @@ public function buildQuery($query) $sql.=' '.(!empty($query['select']) ? $query['select'] : '*'); if(!empty($query['from'])) $sql.="\nFROM ".$query['from']; - else - throw new CDbException(Yii::t('yii','The DB query must contain the "from" portion.')); if(!empty($query['join'])) $sql.="\n".(is_array($query['join']) ? implode("\n",$query['join']) : $query['join']); if(!empty($query['where'])) @@ -9508,6 +9675,14 @@ public function naturalJoin($table) { return $this->joinInternal('natural join', $table); } + public function naturalLeftJoin($table) + { + return $this->joinInternal('natural left join', $table); + } + public function naturalRightJoin($table) + { + return $this->joinInternal('natural right join', $table); + } public function group($columns) { if(is_string($columns) && strpos($columns,'(')!==false) @@ -9723,9 +9898,9 @@ public function dropForeignKey($name, $table) { return $this->setText($this->getConnection()->getSchema()->dropForeignKey($name, $table))->execute(); } - public function createIndex($name, $table, $column, $unique=false) + public function createIndex($name, $table, $columns, $unique=false) { - return $this->setText($this->getConnection()->getSchema()->createIndex($name, $table, $column, $unique))->execute(); + return $this->setText($this->getConnection()->getSchema()->createIndex($name, $table, $columns, $unique))->execute(); } public function dropIndex($name, $table) { @@ -9927,7 +10102,7 @@ abstract protected function validateAttribute($object,$attribute); public static function createValidator($name,$object,$attributes,$params=array()) { if(is_string($attributes)) - $attributes=preg_split('/[\s,]+/',$attributes,-1,PREG_SPLIT_NO_EMPTY); + $attributes=preg_split('/\s*,\s*/',$attributes,-1,PREG_SPLIT_NO_EMPTY); if(isset($params['on'])) { if(is_array($params['on'])) diff --git a/framework/zii/widgets/CBaseListView.php b/framework/zii/widgets/CBaseListView.php index 7a6f5cc6b1a..0ed6edff7e2 100644 --- a/framework/zii/widgets/CBaseListView.php +++ b/framework/zii/widgets/CBaseListView.php @@ -72,6 +72,11 @@ abstract class CBaseListView extends CWidget * */ public $summaryText; + /** + * @var string the HTML tag name for the container of the {@link summaryText} property. + * @since 1.1.16 + */ + public $summaryTagName='div'; /** * @var string the message to be displayed when {@link dataProvider} does not have any data. */ @@ -80,8 +85,15 @@ abstract class CBaseListView extends CWidget * @var string the HTML tag name for the container of the {@link emptyText} property. */ public $emptyTagName='span'; + /** + * @var string the CSS class name for the container of the {@link emptyText} property. Defaults to 'empty'. + * @since 1.1.16 + */ + public $emptyCssClass='empty'; /** * @var string the CSS class name for the container of all data item display. Defaults to 'items'. + * Note, this property must not contain false, null or empty string values. Otherwise such values may + * cause undefined behavior. */ public $itemsCssClass='items'; /** @@ -90,6 +102,8 @@ abstract class CBaseListView extends CWidget public $summaryCssClass='summary'; /** * @var string the CSS class name for the pager container. Defaults to 'pager'. + * Note, this property must not contain false, null or empty string values. Otherwise such values may + * cause undefined behavior. */ public $pagerCssClass='pager'; /** @@ -179,7 +193,7 @@ protected function renderSection($matches) public function renderEmptyText() { $emptyText=$this->emptyText===null ? Yii::t('zii','No results found.') : $this->emptyText; - echo CHtml::tag($this->emptyTagName, array('class'=>'empty'), $emptyText); + echo CHtml::tag($this->emptyTagName, array('class'=>$this->emptyCssClass), $emptyText); } /** @@ -205,7 +219,7 @@ public function renderSummary() if(($count=$this->dataProvider->getItemCount())<=0) return; - echo '
    '; + echo CHtml::openTag($this->summaryTagName, array('class'=>$this->summaryCssClass)); if($this->enablePagination) { $pagination=$this->dataProvider->getPagination(); @@ -239,7 +253,7 @@ public function renderSummary() '{pages}'=>1, )); } - echo '
    '; + echo CHtml::closeTag($this->summaryTagName); } /** diff --git a/framework/zii/widgets/CBreadcrumbs.php b/framework/zii/widgets/CBreadcrumbs.php index 20728b4dfc8..bef6b7a862e 100644 --- a/framework/zii/widgets/CBreadcrumbs.php +++ b/framework/zii/widgets/CBreadcrumbs.php @@ -110,13 +110,15 @@ public function run() if(empty($this->links)) return; + $definedLinks = $this->links; + echo CHtml::openTag($this->tagName,$this->htmlOptions)."\n"; $links=array(); if($this->homeLink===null) - $links[]=CHtml::link(Yii::t('zii','Home'),Yii::app()->homeUrl); + $definedLinks=array_merge(array(Yii::t('zii','Home') => Yii::app()->homeUrl),$definedLinks); elseif($this->homeLink!==false) $links[]=$this->homeLink; - foreach($this->links as $label=>$url) + foreach($definedLinks as $label=>$url) { if(is_string($label) || is_array($url)) $links[]=strtr($this->activeLinkTemplate,array( diff --git a/framework/zii/widgets/CDetailView.php b/framework/zii/widgets/CDetailView.php index 56d05a1084e..debf34f2da4 100644 --- a/framework/zii/widgets/CDetailView.php +++ b/framework/zii/widgets/CDetailView.php @@ -77,7 +77,9 @@ class CDetailView extends CWidget * If the below "value" element is specified, this will be ignored. *
  • value: the value to be displayed. If this is not specified, the above "name" element will be used * to retrieve the corresponding attribute value for display. Note that this value will be formatted according - * to the "type" option as described below.
  • + * to the "type" option as described below. This can also be an anonymous function whose return value will be + * used as a value. The signature of the function should be function($data) where data refers to + * the {@link data} property of the detail view widget. *
  • type: the type of the attribute that determines how the attribute value would be formatted. * Please see above for possible values. *
  • cssClass: the CSS class to be used for this item. This option is available since version 1.1.3.
  • diff --git a/framework/zii/widgets/CListView.php b/framework/zii/widgets/CListView.php index c6229456d01..76c5aaa2f31 100644 --- a/framework/zii/widgets/CListView.php +++ b/framework/zii/widgets/CListView.php @@ -22,7 +22,7 @@ * when the user browser disables JavaScript, the sorting and pagination automatically degenerate * to normal page requests and are still functioning as expected. * - * CListView should be used together with a {@link IDataProvider data provider}, preferrably a + * CListView should be used together with a {@link IDataProvider data provider}, preferably a * {@link CActiveDataProvider}. * * The minimal code needed to use CListView is as follows: @@ -140,7 +140,7 @@ class CListView extends CBaseListView * Example (add in a call to CGridView): *
     	 *  ...
    -	 *  'ajaxUpdateError'=>'function(xhr,ts,et,err){ $("#myerrordiv").text(err); }',
    +	 *  'ajaxUpdateError'=>'function(xhr,ts,et,err,id){ $("#"+id).text(err); }',
     	 *  ...
     	 * 
    * @since 1.1.13 diff --git a/framework/zii/widgets/CMenu.php b/framework/zii/widgets/CMenu.php index 79a32343620..bf14c430b8c 100644 --- a/framework/zii/widgets/CMenu.php +++ b/framework/zii/widgets/CMenu.php @@ -22,7 +22,7 @@ * $this->widget('zii.widgets.CMenu', array( * 'items'=>array( * // Important: you need to specify url as 'controller/action', - * // not just as 'controller' even if default acion is used. + * // not just as 'controller' even if default action is used. * array('label'=>'Home', 'url'=>array('site/index')), * // 'Products' menu item will be selected no matter which tag parameter value is since it's not specified. * array('label'=>'Products', 'url'=>array('product/index'), 'items'=>array( @@ -46,8 +46,10 @@ class CMenu extends CWidget * @var array list of menu items. Each menu item is specified as an array of name-value pairs. * Possible option names include the following: *
      - *
    • label: string, optional, specifies the menu item label. When {@link encodeLabel} is true, the label + *
    • label: string, optional, specifies the menu item label. When {@link encodeLabel} or own encodeLabel option is true, the label * will be HTML-encoded. If the label is not specified, it defaults to an empty string.
    • + *
    • encodeLabel: boolean whether the label for menu item should be HTML-encoded. + * When this option is set, it will override the global setting {@link encodeLabel}. This option has been available since version 1.1.15.
    • *
    • url: string or array, optional, specifies the URL of the menu item. It is passed to {@link CHtml::normalizeUrl} * to generate a valid URL. If this is not set, the menu item will be rendered as a span text.
    • *
    • visible: boolean, optional, whether this menu item is visible. Defaults to true. @@ -269,7 +271,8 @@ protected function normalizeItems($items,$route,&$active) } if(!isset($item['label'])) $item['label']=''; - if($this->encodeLabel) + $encodeLabel = isset($item['encodeLabel']) ? $item['encodeLabel'] : $this->encodeLabel; + if($encodeLabel) $items[$i]['label']=CHtml::encode($item['label']); $hasActiveChild=false; if(isset($item['items'])) diff --git a/framework/zii/widgets/assets/gridview/jquery.yiigridview.js b/framework/zii/widgets/assets/gridview/jquery.yiigridview.js index af47c8f832c..8b9ffc37b7f 100644 --- a/framework/zii/widgets/assets/gridview/jquery.yiigridview.js +++ b/framework/zii/widgets/assets/gridview/jquery.yiigridview.js @@ -54,6 +54,8 @@ ajaxUpdate: [], ajaxVar: 'ajax', ajaxType: 'GET', + csrfTokenName: null, + csrfToken: null, pagerClass: 'pager', loadingClass: 'loading', filterClass: 'filters', @@ -89,11 +91,16 @@ // Check to see if History.js is enabled for our Browser if (settings.enableHistory && window.History.enabled) { // Ajaxify this link - var url = $(this).attr('href').split('?'), - params = $.deparam.querystring('?'+ (url[1] || '')); + var href = $(this).attr('href'); + if(href){ + var url = href.split('?'), + params = $.deparam.querystring('?'+ (url[1] || '')); - delete params[settings.ajaxVar]; - window.History.pushState(null, document.title, decodeURIComponent($.param.querystring(url[0], params))); + delete params[settings.ajaxVar]; + + var updateUrl = $.param.querystring(url[0], params); + window.History.pushState({url: updateUrl}, document.title, decodeURIComponent(updateUrl)); + } } else { $('#' + id).yiiGridView('update', {url: $(this).attr('href')}); } @@ -125,7 +132,9 @@ params = $.deparam.querystring($.param.querystring(url, data)); delete params[settings.ajaxVar]; - window.History.pushState(null, document.title, decodeURIComponent($.param.querystring(url.substr(0, url.indexOf('?')), params))); + + var updateUrl = $.param.querystring(url.substr(0, url.indexOf('?')), params); + window.History.pushState({url: updateUrl}, document.title, decodeURIComponent(updateUrl)); } else { $('#' + id).yiiGridView('update', {data: data}); } @@ -135,7 +144,10 @@ if (settings.enableHistory && settings.ajaxUpdate !== false && window.History.enabled) { $(window).bind('statechange', function() { // Note: We are using statechange instead of popstate var State = window.History.getState(); // Note: We are using History.getState() instead of event.state - $('#' + id).yiiGridView('update', {url: State.url}); + if (State.data.url === undefined) { + State.data.url = State.url; + } + $('#' + id).yiiGridView('update', State.data); }); } @@ -299,7 +311,7 @@ } if (settings.ajaxUpdateError !== undefined) { - settings.ajaxUpdateError(XHR, textStatus, errorThrown, err); + settings.ajaxUpdateError(XHR, textStatus, errorThrown, err, id); } else if (err) { alert(err); } @@ -315,6 +327,12 @@ options.data = $(settings.filterSelector).serialize(); } } + if (settings.csrfTokenName && settings.csrfToken) { + if (typeof options.data=='string') + options.data+='&'+settings.csrfTokenName+'='+settings.csrfToken; + else + options.data[settings.csrfTokenName] = settings.csrfToken; + } if(yiiXHR[id] != null){ yiiXHR[id].abort(); } diff --git a/framework/zii/widgets/assets/listview/jquery.yiilistview.js b/framework/zii/widgets/assets/listview/jquery.yiilistview.js index 9c8527a24f1..20e02467fc5 100644 --- a/framework/zii/widgets/assets/listview/jquery.yiilistview.js +++ b/framework/zii/widgets/assets/listview/jquery.yiilistview.js @@ -35,11 +35,16 @@ if(settings.ajaxUpdate.length > 0) { $(document).on('click.yiiListView', settings.updateSelector,function(){ if(settings.enableHistory && window.History.enabled) { - var url = $(this).attr('href').split('?'), - params = $.deparam.querystring('?'+ (url[1] || '')); + var href = $(this).attr('href'); + if(href){ + var url = href.split('?'), + params = $.deparam.querystring('?'+ (url[1] || '')); - delete params[settings.ajaxVar]; - window.History.pushState(null, document.title, decodeURIComponent($.param.querystring(url[0], params))); + delete params[settings.ajaxVar]; + + var updateUrl = $.param.querystring(url[0], params); + window.History.pushState({url: updateUrl}, document.title, decodeURIComponent(updateUrl)); + } } else { $.fn.yiiListView.update(id, {url: $(this).attr('href')}); } @@ -49,7 +54,10 @@ if(settings.enableHistory && window.History.enabled) { $(window).bind('statechange', function() { // Note: We are using statechange instead of popstate var State = window.History.getState(); // Note: We are using History.getState() instead of event.state - $.fn.yiiListView.update(id, {url: State.url}); + if (State.data.url === undefined) { + State.data.url = State.url; + } + $.fn.yiiListView.update(id, State.data); }); } } @@ -152,29 +160,29 @@ } if (settings.ajaxUpdateError !== undefined) { - settings.ajaxUpdateError(XHR, textStatus, errorThrown, err); + settings.ajaxUpdateError(XHR, textStatus, errorThrown, err, id); } else if (err) { alert(err); } } }, options || {}); - + if(options.data!=undefined && options.type=='GET') { options.url = $.param.querystring(options.url, options.data); options.data = {}; } - + if(settings.ajaxVar) options.url = $.param.querystring(options.url, settings.ajaxVar+'='+id); - + if(yiiXHR[id] != null) { - yiiXHR[id].abort(); + yiiXHR[id].abort(); } - + $('#'+id).addClass(settings.loadingClass); if(settings.beforeAjaxUpdate != undefined) - settings.beforeAjaxUpdate(id); + settings.beforeAjaxUpdate(id, options); yiiXHR[id] = $.ajax(options); }; diff --git a/framework/zii/widgets/grid/CButtonColumn.php b/framework/zii/widgets/grid/CButtonColumn.php index 20ca54b38da..6f31bcc8ec8 100644 --- a/framework/zii/widgets/grid/CButtonColumn.php +++ b/framework/zii/widgets/grid/CButtonColumn.php @@ -152,7 +152,7 @@ class CButtonColumn extends CGridColumn *
       	 *  array(
       	 *     class'=>'CButtonColumn',
      -	 *     'afterDelete'=>'function(link,success,data){ if(success) alert("Delete completed successfuly"); }',
      +	 *     'afterDelete'=>'function(link,success,data){ if(success) alert("Delete completed successfully"); }',
       	 *  ),
       	 * 
      */ @@ -305,13 +305,15 @@ protected function registerClientScript() } /** - * Renders the data cell content. + * Returns the data cell content. * This method renders the view, update and delete buttons in the data cell. * @param integer $row the row number (zero-based) - * @param mixed $data the data associated with the row + * @return string the data cell content. + * @since 1.1.16 */ - protected function renderDataCellContent($row,$data) + public function getDataCellContent($row) { + $data=$this->grid->dataProvider->data[$row]; $tr=array(); ob_start(); foreach($this->buttons as $id=>$button) @@ -321,7 +323,7 @@ protected function renderDataCellContent($row,$data) ob_clean(); } ob_end_clean(); - echo strtr($this->template,$tr); + return strtr($this->template,$tr); } /** diff --git a/framework/zii/widgets/grid/CCheckBoxColumn.php b/framework/zii/widgets/grid/CCheckBoxColumn.php index 5a582bdf442..57134f9c6e6 100644 --- a/framework/zii/widgets/grid/CCheckBoxColumn.php +++ b/framework/zii/widgets/grid/CCheckBoxColumn.php @@ -187,43 +187,39 @@ public function init() } /** - * Renders the header cell content. + * Returns the header cell content. * This method will render a checkbox in the header when {@link selectableRows} is greater than 1 * or in case {@link selectableRows} is null when {@link CGridView::selectableRows} is greater than 1. + * @return string the header cell content. + * @since 1.1.16 */ - protected function renderHeaderCellContent() + public function getHeaderCellContent() { if(trim($this->headerTemplate)==='') - { - echo $this->grid->blankDisplay; - return; - } + return $this->grid->blankDisplay; - $item = ''; if($this->selectableRows===null && $this->grid->selectableRows>1) - $item = CHtml::checkBox($this->id.'_all',false,array('class'=>'select-on-check-all')); + $item=CHtml::checkBox($this->id.'_all',false,array('class'=>'select-on-check-all')); elseif($this->selectableRows>1) - $item = CHtml::checkBox($this->id.'_all',false); + $item=CHtml::checkBox($this->id.'_all',false); else - { - ob_start(); - parent::renderHeaderCellContent(); - $item = ob_get_clean(); - } + $item=parent::getHeaderCellContent(); - echo strtr($this->headerTemplate,array( + return strtr($this->headerTemplate,array( '{item}'=>$item, )); } /** - * Renders the data cell content. + * Returns the data cell content. * This method renders a checkbox in the data cell. * @param integer $row the row number (zero-based) - * @param mixed $data the data associated with the row + * @return string the data cell content. + * @since 1.1.16 */ - protected function renderDataCellContent($row,$data) + public function getDataCellContent($row) { + $data=$this->grid->dataProvider->data[$row]; if($this->value!==null) $value=$this->evaluateExpression($this->value,array('data'=>$data,'row'=>$row)); elseif($this->name!==null) @@ -243,6 +239,6 @@ protected function renderDataCellContent($row,$data) unset($options['name']); $options['value']=$value; $options['id']=$this->id.'_'.$row; - echo CHtml::checkBox($name,$checked,$options); + return CHtml::checkBox($name,$checked,$options); } } diff --git a/framework/zii/widgets/grid/CDataColumn.php b/framework/zii/widgets/grid/CDataColumn.php index 7d2abc91fd7..3d8fc81a1f7 100644 --- a/framework/zii/widgets/grid/CDataColumn.php +++ b/framework/zii/widgets/grid/CDataColumn.php @@ -85,58 +85,63 @@ public function init() } /** - * Renders the filter cell content. - * This method will render the {@link filter} as is if it is a string. + * Returns the filter cell content. + * This method will return the {@link filter} as is if it is a string. * If {@link filter} is an array, it is assumed to be a list of options, and a dropdown selector will be rendered. * Otherwise if {@link filter} is not false, a text field is rendered. - * @since 1.1.1 + * @return string the filter cell content + * @since 1.1.16 */ - protected function renderFilterCellContent() + public function getFilterCellContent() { if(is_string($this->filter)) - echo $this->filter; + return $this->filter; elseif($this->filter!==false && $this->grid->filter!==null && $this->name!==null && strpos($this->name,'.')===false) { if(is_array($this->filter)) - echo CHtml::activeDropDownList($this->grid->filter, $this->name, $this->filter, array('id'=>false,'prompt'=>'')); + return CHtml::activeDropDownList($this->grid->filter, $this->name, $this->filter, array('id'=>false,'prompt'=>'')); elseif($this->filter===null) - echo CHtml::activeTextField($this->grid->filter, $this->name, array('id'=>false)); + return CHtml::activeTextField($this->grid->filter, $this->name, array('id'=>false)); } else - parent::renderFilterCellContent(); + return parent::getFilterCellContent(); } /** - * Renders the header cell content. + * Returns the header cell content. * This method will render a link that can trigger the sorting if the column is sortable. + * @return string the header cell content. + * @since 1.1.16 */ - protected function renderHeaderCellContent() + public function getHeaderCellContent() { if($this->grid->enableSorting && $this->sortable && $this->name!==null) - echo $this->grid->dataProvider->getSort()->link($this->name,$this->header,array('class'=>'sort-link')); + return $this->grid->dataProvider->getSort()->link($this->name,$this->header,array('class'=>'sort-link')); elseif($this->name!==null && $this->header===null) { if($this->grid->dataProvider instanceof CActiveDataProvider) - echo CHtml::encode($this->grid->dataProvider->model->getAttributeLabel($this->name)); + return CHtml::encode($this->grid->dataProvider->model->getAttributeLabel($this->name)); else - echo CHtml::encode($this->name); + return CHtml::encode($this->name); } else - parent::renderHeaderCellContent(); + return parent::getHeaderCellContent(); } /** - * Renders the data cell content. + * Returns the data cell content. * This method evaluates {@link value} or {@link name} and renders the result. * @param integer $row the row number (zero-based) - * @param mixed $data the data associated with the row + * @return string the data cell content. + * @since 1.1.16 */ - protected function renderDataCellContent($row,$data) + public function getDataCellContent($row) { + $data=$this->grid->dataProvider->data[$row]; if($this->value!==null) $value=$this->evaluateExpression($this->value,array('data'=>$data,'row'=>$row)); elseif($this->name!==null) $value=CHtml::value($data,$this->name); - echo $value===null ? $this->grid->nullDisplay : $this->grid->getFormatter()->format($value,$this->type); + return $value===null ? $this->grid->nullDisplay : $this->grid->getFormatter()->format($value,$this->type); } } diff --git a/framework/zii/widgets/grid/CGridColumn.php b/framework/zii/widgets/grid/CGridColumn.php index 48e3d613af4..277c88fae0b 100644 --- a/framework/zii/widgets/grid/CGridColumn.php +++ b/framework/zii/widgets/grid/CGridColumn.php @@ -20,6 +20,9 @@ * * @property boolean $hasFooter Whether this column has a footer cell. * This is determined based on whether {@link footer} is set. + * @property string $filterCellContent The filter cell content. + * @property string $headerCellContent The header cell content. + * @property string $footerCellContent The footer cell content. * * @author Qiang Xue * @package zii.widgets.grid @@ -163,44 +166,90 @@ public function renderFooterCell() } /** - * Renders the header cell content. - * The default implementation simply renders {@link header}. + * Returns the header cell content. + * The default implementation simply returns {@link header}. * This method may be overridden to customize the rendering of the header cell. + * @return string the header cell content. + * @since 1.1.16 + */ + public function getHeaderCellContent() + { + return trim($this->header)!=='' ? $this->header : $this->grid->blankDisplay; + } + + /** + * Renders the header cell content. + * @deprecated since 1.1.16. Use {@link getHeaderCellContent()} instead. */ protected function renderHeaderCellContent() { - echo trim($this->header)!=='' ? $this->header : $this->grid->blankDisplay; + echo $this->getHeaderCellContent(); } /** - * Renders the footer cell content. - * The default implementation simply renders {@link footer}. + * Returns the footer cell content. + * The default implementation simply returns {@link footer}. * This method may be overridden to customize the rendering of the footer cell. + * @return string the footer cell content. + * @since 1.1.16 + */ + public function getFooterCellContent() + { + return trim($this->footer)!=='' ? $this->footer : $this->grid->blankDisplay; + } + + /** + * Renders the footer cell content. + * @deprecated since 1.1.16. Use {@link getFooterCellContent()} instead. */ protected function renderFooterCellContent() { - echo trim($this->footer)!=='' ? $this->footer : $this->grid->blankDisplay; + echo $this->getFooterCellContent(); } /** - * Renders the data cell content. + * Returns the data cell content. * This method SHOULD be overridden to customize the rendering of the data cell. * @param integer $row the row number (zero-based) + * The data for this row is available via $this->grid->dataProvider->data[$row]; + * @return string the data cell content. + * @since 1.1.16 + */ + public function getDataCellContent($row) + { + return $this->grid->blankDisplay; + } + + /** + * Renders the data cell content. + * @param integer $row the row number (zero-based) * @param mixed $data the data associated with the row + * @deprecated since 1.1.16. Use {@link getDataCellContent()} instead. */ protected function renderDataCellContent($row,$data) { - echo $this->grid->blankDisplay; + echo $this->getDataCellContent($row); } /** - * Renders the filter cell content. - * The default implementation simply renders a space. + * Returns the filter cell content. + * The default implementation simply returns an empty column. * This method may be overridden to customize the rendering of the filter cell (if any). + * @return string the filter cell content. + * @since 1.1.16 + */ + public function getFilterCellContent() + { + return $this->grid->blankDisplay; + } + + /** + * Renders the filter cell content. * @since 1.1.1 + * @deprecated since 1.1.16. Use {@link getFilterCellContent()} instead. */ protected function renderFilterCellContent() { - echo $this->grid->blankDisplay; + echo $this->getFilterCellContent(); } } diff --git a/framework/zii/widgets/grid/CGridView.php b/framework/zii/widgets/grid/CGridView.php index 1a44182aaf4..eeb11b77f6b 100644 --- a/framework/zii/widgets/grid/CGridView.php +++ b/framework/zii/widgets/grid/CGridView.php @@ -25,7 +25,7 @@ * when the user browser disables JavaScript, the sorting and pagination automatically degenerate * to normal page requests and are still functioning as expected. * - * CGridView should be used together with a {@link IDataProvider data provider}, preferrably a + * CGridView should be used together with a {@link IDataProvider data provider}, preferably a * {@link CActiveDataProvider}. * * The minimal code needed to use CGridView is as follows: @@ -182,14 +182,14 @@ class CGridView extends CBaseListView * Possible values (besides null) are "timeout", "error", "notmodified" and "parsererror"
    • *
    • errorThrown is an optional exception object, if one occurred.
    • *
    • errorMessage is the CGridView default error message derived from xhr and errorThrown. - * Usefull if you just want to display this error differently. CGridView by default displays this error with an javascript.alert()
    • + * Useful if you just want to display this error differently. CGridView by default displays this error with an javascript.alert() *
    * Note: This handler is not called for JSONP requests, because they do not use an XMLHttpRequest. * * Example (add in a call to CGridView): *
     	 *  ...
    -	 *  'ajaxUpdateError'=>'function(xhr,ts,et,err){ $("#myerrordiv").text(err); }',
    +	 *  'ajaxUpdateError'=>'function(xhr,ts,et,err,id){ $("#"+id).text(err); }',
     	 *  ...
     	 * 
    */ @@ -444,8 +444,14 @@ public function registerClientScript() ); if($this->ajaxUrl!==null) $options['url']=CHtml::normalizeUrl($this->ajaxUrl); - if($this->ajaxType!==null) + if($this->ajaxType!==null) { $options['ajaxType']=strtoupper($this->ajaxType); + $request=Yii::app()->getRequest(); + if ($options['ajaxType']=='POST' && $request->enableCsrfValidation) { + $options['csrfTokenName']=$request->csrfTokenName; + $options['csrfToken']=$request->getCsrfToken(); + } + } if($this->enablePagination) $options['pageVar']=$this->dataProvider->getPagination()->pageVar; foreach(array('beforeAjaxUpdate', 'afterAjaxUpdate', 'ajaxUpdateError', 'selectionChanged') as $event) @@ -613,10 +619,25 @@ public function renderTableRow($row) echo CHtml::openTag('tr', $htmlOptions)."\n"; foreach($this->columns as $column) - $column->renderDataCell($row); + $this->renderDataCell($column, $row); echo "\n"; } + /** + * A seam for people extending CGridView to be able to hook onto the data cell rendering process. + * + * By overriding only this method we will not need to copypaste and modify the whole entirety of `renderTableRow`. + * Or override `renderDataCell()` method of all possible CGridColumn descendants. + * + * @param CGridColumn $column The Column instance to + * @param integer $row + * @since 1.1.17 + */ + protected function renderDataCell($column, $row) + { + $column->renderDataCell($row); + } + /** * @return boolean whether the table should render a footer. * This is true if any of the {@link columns} has a true {@link CGridColumn::hasFooter} value. diff --git a/framework/zii/widgets/grid/CLinkColumn.php b/framework/zii/widgets/grid/CLinkColumn.php index f73b8a23af9..c8c47b462ee 100644 --- a/framework/zii/widgets/grid/CLinkColumn.php +++ b/framework/zii/widgets/grid/CLinkColumn.php @@ -87,13 +87,15 @@ class CLinkColumn extends CGridColumn public $linkHtmlOptions=array(); /** - * Renders the data cell content. + * Returns the data cell content. * This method renders a hyperlink in the data cell. * @param integer $row the row number (zero-based) - * @param mixed $data the data associated with the row + * @return string the data cell content. + * @since 1.1.16 */ - protected function renderDataCellContent($row,$data) + public function getDataCellContent($row) { + $data=$this->grid->dataProvider->data[$row]; if($this->urlExpression!==null) $url=$this->evaluateExpression($this->urlExpression,array('data'=>$data,'row'=>$row)); else @@ -104,8 +106,8 @@ protected function renderDataCellContent($row,$data) $label=$this->label; $options=$this->linkHtmlOptions; if(is_string($this->imageUrl)) - echo CHtml::link(CHtml::image($this->imageUrl,$label),$url,$options); + return CHtml::link(CHtml::image($this->imageUrl,$label),$url,$options); else - echo CHtml::link($label,$url,$options); + return CHtml::link($label,$url,$options); } } diff --git a/framework/zii/widgets/jui/CJuiAccordion.php b/framework/zii/widgets/jui/CJuiAccordion.php index e76cc6e36ee..b3c3f49f517 100644 --- a/framework/zii/widgets/jui/CJuiAccordion.php +++ b/framework/zii/widgets/jui/CJuiAccordion.php @@ -28,7 +28,7 @@ * ), * // additional javascript options for the accordion plugin * 'options'=>array( - * 'animated'=>'bounceslide', + * 'animate'=>'bounceslide', * ), * )); * diff --git a/framework/zii/widgets/jui/CJuiTabs.php b/framework/zii/widgets/jui/CJuiTabs.php index 72317c86611..90e2dd2a9ae 100644 --- a/framework/zii/widgets/jui/CJuiTabs.php +++ b/framework/zii/widgets/jui/CJuiTabs.php @@ -82,7 +82,7 @@ class CJuiTabs extends CJuiWidget * The token "{title}" in the template will be replaced with the panel title and * the token "{url}" will be replaced with "#TabID" or with the url of the ajax request. */ - public $headerTemplate='
  • {title}
  • '; + public $headerTemplate='
  • {title}
  • '; /** * @var string the template that is used to generated every tab content. * The token "{content}" in the template will be replaced with the panel content @@ -135,15 +135,4 @@ public function run() $options=CJavaScript::encode($this->options); Yii::app()->getClientScript()->registerScript(__CLASS__.'#'.$id,"jQuery('#{$id}').tabs($options);"); } - - /** - * Registers the core script files. - * This method overrides the parent implementation by registering the cookie plugin when cookie option is used. - */ - protected function registerCoreScripts() - { - parent::registerCoreScripts(); - if(isset($this->options['cookie'])) - Yii::app()->getClientScript()->registerCoreScript('cookie'); - } } \ No newline at end of file